* [PATCH v2 01/15] tests: Check each error when merging a dts file
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 02/15] srcpos: Allow srcpos_string() to accept NULL Simon Glass
` (13 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
When the parser merges the top-level fragments of a dts file, it can
report that a label or path is not found, or that a label-relative
reference is not supported in a plugin. Most of these errors are not
covered by any test, and the two tests which do reach them only check
that dtc fails, not the message or where it is reported. The merge
logic is about to move out of the grammar, so this needs better tests.
Add a test file for each error which is not covered, and check the
message and its position for all of them, including the two existing
ones. Also check that merging carries on after an error, so that later
errors are reported too, that a reference error is still reported when
the parser finds another error, that a labelled reference within a
plugin adds the label, that / { ... } and &{/} { ... } differ in a
plugin, and that a label in a plugin becomes an overlay fragment if it
is not found or is written as &{label}, which applies after any local
merges, since nothing covers these either.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Reword four comments in the test files
- Add merge-root-plugin.dts to check / { ... } against &{/} { ... }
- Add merge-label-base.dts to check a label which is not in the plugin
- Add merge-label-braces.dts to check &{label} in a plugin
- Add merge-label-order.dts to check the order of &{label} and &label
tests/merge-delete-missing.dts | 9 ++++
tests/merge-errors.dts | 14 +++++++
tests/merge-first-ref-relative.dts | 10 +++++
tests/merge-first-ref.dts | 9 ++++
tests/merge-label-base.dts | 10 +++++
tests/merge-label-braces.dts | 12 ++++++
tests/merge-label-order.dts | 17 ++++++++
tests/merge-labelled-plugin.dts | 12 ++++++
tests/merge-labelled-relative.dts | 14 +++++++
tests/merge-omit-missing.dts | 9 ++++
tests/merge-parse-error.dts | 17 ++++++++
tests/merge-path-ref-missing.dts | 11 +++++
tests/merge-path-relative.dts | 12 ++++++
tests/merge-root-plugin.dts | 12 ++++++
tests/run_tests.sh | 67 ++++++++++++++++++++++++++++++
15 files changed, 235 insertions(+)
create mode 100644 tests/merge-delete-missing.dts
create mode 100644 tests/merge-errors.dts
create mode 100644 tests/merge-first-ref-relative.dts
create mode 100644 tests/merge-first-ref.dts
create mode 100644 tests/merge-label-base.dts
create mode 100644 tests/merge-label-braces.dts
create mode 100644 tests/merge-label-order.dts
create mode 100644 tests/merge-labelled-plugin.dts
create mode 100644 tests/merge-labelled-relative.dts
create mode 100644 tests/merge-omit-missing.dts
create mode 100644 tests/merge-parse-error.dts
create mode 100644 tests/merge-path-ref-missing.dts
create mode 100644 tests/merge-path-relative.dts
create mode 100644 tests/merge-root-plugin.dts
diff --git a/tests/merge-delete-missing.dts b/tests/merge-delete-missing.dts
new file mode 100644
index 0000000..b36e39e
--- /dev/null
+++ b/tests/merge-delete-missing.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+ label: node {
+ };
+};
+
+/* Try to delete a node using a non-existent label */
+/delete-node/ &nosuchnode;
diff --git a/tests/merge-errors.dts b/tests/merge-errors.dts
new file mode 100644
index 0000000..1e97088
--- /dev/null
+++ b/tests/merge-errors.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+ label: node {
+ };
+};
+
+/* All three of the references below are bad, and each one is reported */
+&nosuchnode1 {
+ prop;
+};
+
+/delete-node/ &nosuchnode2;
+/omit-if-no-ref/ &nosuchnode3;
diff --git a/tests/merge-first-ref-relative.dts b/tests/merge-first-ref-relative.dts
new file mode 100644
index 0000000..eece7a7
--- /dev/null
+++ b/tests/merge-first-ref-relative.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+/plugin/;
+
+/* A plugin cannot define a node using a label-relative reference */
+&{label/node} {
+ prop;
+};
+
+/* Later fragments are still merged */
+/delete-node/ &nosuchnode;
diff --git a/tests/merge-first-ref.dts b/tests/merge-first-ref.dts
new file mode 100644
index 0000000..8f23a44
--- /dev/null
+++ b/tests/merge-first-ref.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/* Only a plugin can start with a reference */
+&nosuchnode {
+ prop;
+};
+
+/* Later fragments are still merged */
+/delete-node/ &nosuchnode2;
diff --git a/tests/merge-label-base.dts b/tests/merge-label-base.dts
new file mode 100644
index 0000000..1a0a8bb
--- /dev/null
+++ b/tests/merge-label-base.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+};
+
+/* A label which is not in the plugin may be in the base tree */
+&{baselabel} {
+ prop;
+};
diff --git a/tests/merge-label-braces.dts b/tests/merge-label-braces.dts
new file mode 100644
index 0000000..d5f8d0f
--- /dev/null
+++ b/tests/merge-label-braces.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ label: node {
+ };
+};
+
+/* This becomes an overlay fragment, even though the label is in the plugin */
+&{label} {
+ prop;
+};
diff --git a/tests/merge-label-order.dts b/tests/merge-label-order.dts
new file mode 100644
index 0000000..f7923a0
--- /dev/null
+++ b/tests/merge-label-order.dts
@@ -0,0 +1,17 @@
+/dts-v1/;
+/plugin/;
+
+&basenode {
+ label: foo {
+ p = "first";
+ };
+};
+
+/* The fragment for &{label} is applied last, so "second" wins */
+&{label} {
+ p = "second";
+};
+
+&label {
+ p = "third";
+};
diff --git a/tests/merge-labelled-plugin.dts b/tests/merge-labelled-plugin.dts
new file mode 100644
index 0000000..3849cc5
--- /dev/null
+++ b/tests/merge-labelled-plugin.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ label: node {
+ };
+};
+
+/* Add a label to a node defined in the plugin itself */
+newlabel: &label {
+ prop = "value";
+};
diff --git a/tests/merge-labelled-relative.dts b/tests/merge-labelled-relative.dts
new file mode 100644
index 0000000..9bedfd9
--- /dev/null
+++ b/tests/merge-labelled-relative.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ label: node {
+ subnode {
+ };
+ };
+};
+
+/* Not allowed even though the reference is found */
+newlabel: &{label/subnode} {
+ prop;
+};
diff --git a/tests/merge-omit-missing.dts b/tests/merge-omit-missing.dts
new file mode 100644
index 0000000..0357648
--- /dev/null
+++ b/tests/merge-omit-missing.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+ label: node {
+ };
+};
+
+/* Try to mark a node using a non-existent path */
+/omit-if-no-ref/ &{/nosuchnode};
diff --git a/tests/merge-parse-error.dts b/tests/merge-parse-error.dts
new file mode 100644
index 0000000..c39318b
--- /dev/null
+++ b/tests/merge-parse-error.dts
@@ -0,0 +1,17 @@
+/dts-v1/;
+
+/ {
+ label: node {
+ };
+};
+
+/* Reported even though the parser finds another error below */
+&nosuchnode {
+ prop;
+};
+
+/ {
+ node {
+ prop = <(1/0)>;
+ };
+};
diff --git a/tests/merge-path-ref-missing.dts b/tests/merge-path-ref-missing.dts
new file mode 100644
index 0000000..ad2e35e
--- /dev/null
+++ b/tests/merge-path-ref-missing.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+ node {
+ };
+};
+
+/* Try to redefine a node using a non-existent path */
+&{/nosuchnode} {
+ prop;
+};
diff --git a/tests/merge-path-relative.dts b/tests/merge-path-relative.dts
new file mode 100644
index 0000000..c4e00a4
--- /dev/null
+++ b/tests/merge-path-relative.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ label: node {
+ };
+};
+
+/* A plugin cannot define a node using a label-relative path reference */
+&{label/node} {
+ prop;
+};
diff --git a/tests/merge-root-plugin.dts b/tests/merge-root-plugin.dts
new file mode 100644
index 0000000..f67355e
--- /dev/null
+++ b/tests/merge-root-plugin.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+/plugin/;
+
+/* This is in the root node of the plugin */
+/ {
+ prop1;
+};
+
+/* This is an overlay fragment for the root node of the base tree */
+&{/} {
+ prop2;
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f750faa..7beb6ae 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -770,6 +770,73 @@ dtc_tests () {
run_sh_test "$SRCDIR/dtc-checkfails.sh" phandle_references -- -I dts -O dtb "$SRCDIR/nonexist-label-ref.dts"
run_sh_test "$SRCDIR/dtc-fatal.sh" -m "Label or path nosuchnode not found" \
-I dts -O dtb "$SRCDIR/nonexist-node-ref2.dts"
+
+ # Check each error when merging the fragments of a dts file, with its
+ # position
+ run_wrap_error_msg_test "test_label_ref.dts:7.8-15 Label or path handle not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/test_label_ref.dts"
+ run_wrap_error_msg_test "nonexist-node-ref2.dts:9.1-12 Label or path nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/nonexist-node-ref2.dts"
+ run_wrap_error_msg_test "merge-first-ref.dts:4.13-6.3 Label or path nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-first-ref.dts"
+ run_wrap_error_msg_test "merge-first-ref.dts:9.15-27 Label or path nosuchnode2 not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-first-ref.dts"
+ run_wrap_error_msg_test "merge-first-ref-relative.dts:5.15-7.3 Label-relative reference label/node not supported in plugin" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-first-ref-relative.dts"
+ run_wrap_error_msg_test "merge-first-ref-relative.dts:10.15-26 Label or path nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-first-ref-relative.dts"
+ run_wrap_error_msg_test "merge-labelled-relative.dts:12.1-10 Label-relative reference label/subnode not supported in plugin" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-labelled-relative.dts"
+ run_wrap_error_msg_test "merge-path-relative.dts:10.1-14 Label-relative reference label/node not supported in plugin" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-path-relative.dts"
+ run_wrap_error_msg_test "merge-path-ref-missing.dts:9.1-15 Label or path /nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-path-ref-missing.dts"
+ run_wrap_error_msg_test "merge-delete-missing.dts:9.15-26 Label or path nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-delete-missing.dts"
+ run_wrap_error_msg_test "merge-omit-missing.dts:9.18-32 Label or path /nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-omit-missing.dts"
+ run_wrap_error_msg_test "merge-errors.dts:9.1-13 Label or path nosuchnode1 not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-errors.dts"
+ run_wrap_error_msg_test "merge-errors.dts:13.15-27 Label or path nosuchnode2 not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-errors.dts"
+ run_wrap_error_msg_test "merge-errors.dts:14.18-30 Label or path nosuchnode3 not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-errors.dts"
+ run_wrap_error_msg_test "merge-parse-error.dts:9.1-12 Label or path nosuchnode not found" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-parse-error.dts"
+ run_wrap_error_msg_test "merge-parse-error.dts:15.12-15 Division by zero" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/merge-parse-error.dts"
+
+ # A labelled reference to a node in a plugin adds the label to it
+ run_dtc_test -@ -I dts -O dtb -o merge-labelled-plugin.test.dtb \
+ "$SRCDIR/merge-labelled-plugin.dts"
+ run_fdtget_test "/node" merge-labelled-plugin.test.dtb /__symbols__ newlabel
+ run_fdtget_test "value" merge-labelled-plugin.test.dtb /node prop
+
+ # In a plugin, &{label} becomes an overlay fragment, even if it is found
+ run_dtc_test -I dts -O dtb -o merge-label-braces.test.dtb \
+ "$SRCDIR/merge-label-braces.dts"
+ run_fdtget_test "" merge-label-braces.test.dtb /fragment@0/__overlay__ prop
+ run_fdtget_test "node\nfragment@0\n__local_fixups__" merge-label-braces.test.dtb -l /
+
+ # The fragment for &{label} is applied after any local merges
+ run_dtc_test -I dts -O dtb -o merge-label-order.test.dtb \
+ "$SRCDIR/merge-label-order.dts"
+ run_fdtget_test "third" merge-label-order.test.dtb /fragment@0/__overlay__/foo p
+ run_fdtget_test "second" merge-label-order.test.dtb /fragment@1/__overlay__ p
+
+ # In a plugin, / { ... } and &{/} { ... } are not the same thing
+ run_dtc_test -I dts -O dtb -o merge-root-plugin.test.dtb \
+ "$SRCDIR/merge-root-plugin.dts"
+ run_fdtget_test "" merge-root-plugin.test.dtb / prop1
+ run_fdtget_test "/" merge-root-plugin.test.dtb /fragment@0 target-path
+ run_fdtget_test "" merge-root-plugin.test.dtb /fragment@0/__overlay__ prop2
+
+ # In a plugin, a label which is not found becomes an overlay fragment
+ run_dtc_test -I dts -O dtb -o merge-label-base.test.dtb \
+ "$SRCDIR/merge-label-base.dts"
+ run_fdtget_test "" merge-label-base.test.dtb /fragment@0/__overlay__ prop
+ run_fdtget_test "/fragment@0:target:0" merge-label-base.test.dtb /__fixups__ baselabel
+
check_tests "$SRCDIR/bad-name-property.dts" name_properties
check_tests "$SRCDIR/bad-ncells.dts" address_cells_is_cell size_cells_is_cell interrupts_extended_is_cell
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 02/15] srcpos: Allow srcpos_string() to accept NULL
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
2026-11-04 21:17 ` [PATCH v2 01/15] tests: Check each error when merging a dts file Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 03/15] dtc: Add a struct for the top-level fragments of a dts file Simon Glass
` (12 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
A position is not always available: a node read from a dtb has none, and
top-level fragments may eventually come from a dtbo as well as from
source. At present srcpos_string() crashes if it has no position, so
each caller which might not have one needs to check, as those in
checks.c do.
Return a placeholder in this case, using the same text as
srcpos_string_comment() does, so that srcpos_error() can be used whether
or not there is a position.
No caller passes NULL at present, so this has no test yet.
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Add new patch for srcpos_string()
srcpos.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/srcpos.c b/srcpos.c
index fef892f..c42eba8 100644
--- a/srcpos.c
+++ b/srcpos.c
@@ -324,6 +324,9 @@ srcpos_string(struct srcpos *pos)
const char *fname = "<no-file>";
char *pos_str;
+ if (!pos)
+ return xstrdup("<no-file>:<no-line>");
+
if (pos->file && pos->file->name)
fname = pos->file->name;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 03/15] dtc: Add a struct for the top-level fragments of a dts file
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
2026-11-04 21:17 ` [PATCH v2 01/15] tests: Check each error when merging a dts file Simon Glass
2026-11-04 21:17 ` [PATCH v2 02/15] srcpos: Allow srcpos_string() to accept NULL Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 04/15] dtc: Move the /delete-node/ handling out of the grammar Simon Glass
` (11 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
A dts file is made up of top-level fragments: the root node, later
definitions of a node by label or path, and /delete-node/ or
/omit-if-no-ref/ with a reference. We would like to deal with each
fragment on its own before it is merged, for example to apply deletes
consistently or to run some checks on each one, but that requires
keeping the fragments apart. At present the parser merges each one into
the tree as soon as it is parsed.
Add a struct fragment to hold one of these, with a function to build
each kind, and functions to append one to a list and to free it.
These are not used yet.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
David, you asked why the root needs a special type, rather than setting
ref to "/". This drops the type, but uses a NULL ref rather than "/",
since the two differ in a plugin: / { ... } adds to the plugin's own
root, whereas &{/} { ... } becomes a fragment@N node for the root of the
base tree. Using "/" for both would change the output.
merge-root-plugin.dts checks this.
Changes in v2:
- Replace FRAGMENT_ROOT and FRAGMENT_REF with FRAGMENT_NODE
- Use a NULL ref for the root node
- Drop path_ref in favour of checking the ref for a leading '/'
- Add a constructor for each kind of fragment, taking all its fields
- Add the list to struct dt_info later, when it is first used
- Change the subject from 'Add a list' to 'Add a struct'
dtc.h | 50 ++++++++++++++++++++++++++++++++
livetree.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 135 insertions(+)
diff --git a/dtc.h b/dtc.h
index 473552e..f15a162 100644
--- a/dtc.h
+++ b/dtc.h
@@ -322,6 +322,56 @@ struct reserve_info *chain_reserve_entry(struct reserve_info *first,
struct reserve_info *add_reserve_entry(struct reserve_info *list,
struct reserve_info *new);
+/*
+ * A fragment is one top-level part of a dts file, after the header and any
+ * memory reservations: the root node, a later definition of a node which
+ * refers to it by label or path, or a /delete-node/ or /omit-if-no-ref/ with
+ * a reference. Fragments are kept apart until they are merged into a single
+ * tree.
+ *
+ * This is not the same as an overlay fragment (a fragment@N node), although
+ * in a plugin a fragment may become one when it is merged.
+ */
+
+enum fragment_type {
+ FRAGMENT_NODE, /* / { ... }; or [label:] &ref { ... }; */
+ FRAGMENT_DELETE_NODE, /* /delete-node/ &ref; */
+ FRAGMENT_OMIT_IF_NO_REF, /* /omit-if-no-ref/ &ref; */
+};
+
+/*
+ * Which fields are used depends on the type:
+ *
+ * FRAGMENT_NODE node, plus ref and ref_srcpos unless it is the
+ * root node, plus label and label_srcpos if there
+ * is a label
+ * FRAGMENT_DELETE_NODE ref and ref_srcpos
+ * FRAGMENT_OMIT_IF_NO_REF ref and ref_srcpos
+ *
+ * Unused fields are NULL. For / { ... }; the ref is NULL rather than "/", since
+ * in a plugin &{/} { ... }; refers to the root of the base tree instead.
+ */
+struct fragment {
+ enum fragment_type type;
+ char *ref; /* label or path referred to */
+ struct srcpos *ref_srcpos; /* position of ref */
+ char *label; /* label to add to the target */
+ struct srcpos *label_srcpos; /* position of label */
+ struct node *node; /* the node definition */
+
+ struct fragment *next; /* NULL at the end of the list */
+};
+
+struct fragment *build_fragment_root(struct node *node);
+struct fragment *build_fragment_ref(char *label, struct srcpos *label_srcpos,
+ char *ref, struct srcpos *ref_srcpos,
+ struct node *node);
+struct fragment *build_fragment_delete_node(char *ref,
+ struct srcpos *ref_srcpos);
+struct fragment *build_fragment_omit_if_no_ref(char *ref,
+ struct srcpos *ref_srcpos);
+struct fragment *append_fragment(struct fragment *list, struct fragment *new);
+void free_fragment(struct fragment *f);
struct dt_info {
unsigned int dtsflags;
diff --git a/livetree.c b/livetree.c
index abe73de..85725d3 100644
--- a/livetree.c
+++ b/livetree.c
@@ -448,6 +448,91 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
return list;
}
+/* Create a fragment of the given type, with its other fields NULL */
+static struct fragment *new_fragment(enum fragment_type type, char *ref,
+ struct srcpos *ref_srcpos)
+{
+ struct fragment *new = xmalloc(sizeof(*new));
+
+ memset(new, '\0', sizeof(*new));
+
+ new->type = type;
+ new->ref = ref;
+ new->ref_srcpos = srcpos_copy(ref_srcpos);
+
+ return new;
+}
+
+/* Create a fragment for / { ... }; */
+struct fragment *build_fragment_root(struct node *node)
+{
+ struct fragment *new = new_fragment(FRAGMENT_NODE, NULL, NULL);
+
+ new->node = node;
+
+ return new;
+}
+
+/*
+ * Create a fragment for [label:] &ref { ... }; where label and label_srcpos are
+ * NULL if there is no label
+ */
+struct fragment *build_fragment_ref(char *label, struct srcpos *label_srcpos,
+ char *ref, struct srcpos *ref_srcpos,
+ struct node *node)
+{
+ struct fragment *new = new_fragment(FRAGMENT_NODE, ref, ref_srcpos);
+
+ new->label = label;
+ new->label_srcpos = srcpos_copy(label_srcpos);
+ new->node = node;
+
+ return new;
+}
+
+/* Create a fragment for /delete-node/ &ref; */
+struct fragment *build_fragment_delete_node(char *ref,
+ struct srcpos *ref_srcpos)
+{
+ return new_fragment(FRAGMENT_DELETE_NODE, ref, ref_srcpos);
+}
+
+/* Create a fragment for /omit-if-no-ref/ &ref; */
+struct fragment *build_fragment_omit_if_no_ref(char *ref,
+ struct srcpos *ref_srcpos)
+{
+ return new_fragment(FRAGMENT_OMIT_IF_NO_REF, ref, ref_srcpos);
+}
+
+/* Add a fragment to the end of a list, returning the new head */
+struct fragment *append_fragment(struct fragment *list, struct fragment *new)
+{
+ struct fragment *last;
+
+ new->next = NULL;
+
+ if (!list)
+ return new;
+
+ for (last = list; last->next; last = last->next)
+ ;
+
+ last->next = new;
+
+ return list;
+}
+
+/*
+ * Free a fragment, but not its node, reference or label, since the tree may
+ * still use these
+ */
+void free_fragment(struct fragment *f)
+{
+ srcpos_free(f->ref_srcpos);
+ srcpos_free(f->label_srcpos);
+ free(f);
+}
+
static void fill_fullpaths(struct node *tree, const char *prefix)
{
struct node *child;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 04/15] dtc: Move the /delete-node/ handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (2 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 03/15] dtc: Add a struct for the top-level fragments of a dts file Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 05/15] dtc: Move the /omit-if-no-ref/ " Simon Glass
` (10 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The grammar actions merge each top-level fragment of a dts file into the
tree as it is parsed, so the merge logic is spread over the rules for
each kind of fragment. This makes it hard to follow and to change.
As a first step towards moving this logic into livetree.c, move the
action for a top-level /delete-node/ into a new merge_delete_node(),
passing it a struct fragment for the reference.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Use dt_ref instead of fragment_ref
- Return true on success from merge_delete_node()
- Check the fragment type with assert()
dtc-parser.y | 11 ++++-------
dtc.h | 1 +
livetree.c | 20 ++++++++++++++++++++
3 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index e47f80d..bba98b6 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -238,14 +238,11 @@ devicetree:
}
| devicetree DT_DEL_NODE dt_ref ';'
{
- struct node *target = get_node_by_ref($1, $3);
-
- if (target)
- delete_node(target);
- else
- ERROR(&@3, "Label or path %s not found", $3);
-
+ struct fragment *f;
+ f = build_fragment_delete_node($3, &@3);
+ if (!merge_delete_node($1, f))
+ treesource_error = true;
$$ = $1;
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
diff --git a/dtc.h b/dtc.h
index f15a162..5dca8bc 100644
--- a/dtc.h
+++ b/dtc.h
@@ -372,6 +372,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct srcpos *ref_srcpos);
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
+bool merge_delete_node(struct node *dt, struct fragment *f);
struct dt_info {
unsigned int dtsflags;
diff --git a/livetree.c b/livetree.c
index 85725d3..35beee0 100644
--- a/livetree.c
+++ b/livetree.c
@@ -567,6 +567,26 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
return dti;
}
+/*
+ * Delete the node which a /delete-node/ fragment refers to
+ *
+ * Returns true on success, false on an error, which has been reported.
+ */
+bool merge_delete_node(struct node *dt, struct fragment *f)
+{
+ struct node *target = get_node_by_ref(dt, f->ref);
+
+ assert(f->type == FRAGMENT_DELETE_NODE);
+ if (!target) {
+ srcpos_error(f->ref_srcpos, "Error",
+ "Label or path %s not found", f->ref);
+ return false;
+ }
+ delete_node(target);
+
+ return true;
+}
+
/*
* Tree accessor functions
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 05/15] dtc: Move the /omit-if-no-ref/ handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (3 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 04/15] dtc: Move the /delete-node/ handling out of the grammar Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 06/15] dtc: Move is_ref_relative() to livetree.c Simon Glass
` (9 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The grammar action for a top-level /omit-if-no-ref/ still holds its
merge logic.
Move it into a new merge_omit_if_no_ref(), passing it a struct fragment
for the reference.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Use dt_ref instead of fragment_ref
- Return true on success from merge_omit_if_no_ref()
- Check the fragment type with assert()
dtc-parser.y | 11 ++++-------
dtc.h | 1 +
livetree.c | 21 +++++++++++++++++++++
3 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index bba98b6..1aaff42 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -247,14 +247,11 @@ devicetree:
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
{
- struct node *target = get_node_by_ref($1, $3);
-
- if (target)
- omit_node_if_unused(target);
- else
- ERROR(&@3, "Label or path %s not found", $3);
-
+ struct fragment *f;
+ f = build_fragment_omit_if_no_ref($3, &@3);
+ if (!merge_omit_if_no_ref($1, f))
+ treesource_error = true;
$$ = $1;
}
;
diff --git a/dtc.h b/dtc.h
index 5dca8bc..22a050a 100644
--- a/dtc.h
+++ b/dtc.h
@@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
bool merge_delete_node(struct node *dt, struct fragment *f);
+bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
struct dt_info {
unsigned int dtsflags;
diff --git a/livetree.c b/livetree.c
index 35beee0..a54009c 100644
--- a/livetree.c
+++ b/livetree.c
@@ -587,6 +587,27 @@ bool merge_delete_node(struct node *dt, struct fragment *f)
return true;
}
+/*
+ * Mark the node which an /omit-if-no-ref/ fragment refers to, so that it is
+ * dropped if nothing refers to it
+ *
+ * Returns true on success, false on an error, which has been reported.
+ */
+bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
+{
+ struct node *target = get_node_by_ref(dt, f->ref);
+
+ assert(f->type == FRAGMENT_OMIT_IF_NO_REF);
+ if (!target) {
+ srcpos_error(f->ref_srcpos, "Error",
+ "Label or path %s not found", f->ref);
+ return false;
+ }
+ omit_node_if_unused(target);
+
+ return true;
+}
+
/*
* Tree accessor functions
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 06/15] dtc: Move is_ref_relative() to livetree.c
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (4 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 05/15] dtc: Move the /omit-if-no-ref/ " Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar Simon Glass
` (8 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The remaining grammar actions which merge a fragment use
is_ref_relative() to reject a label-relative reference in a plugin.
Move it to livetree.c, so that it is available when these actions move
there too, and add a comment.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
---
(no changes since v1)
dtc-parser.y | 5 -----
dtc.h | 1 +
livetree.c | 6 ++++++
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 1aaff42..9b9b6db 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -24,11 +24,6 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
-static bool is_ref_relative(const char *ref)
-{
- return ref[0] != '/' && strchr(&ref[1], '/');
-}
-
%}
%union {
diff --git a/dtc.h b/dtc.h
index 22a050a..8c4d7bf 100644
--- a/dtc.h
+++ b/dtc.h
@@ -372,6 +372,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct srcpos *ref_srcpos);
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
+bool is_ref_relative(const char *ref);
bool merge_delete_node(struct node *dt, struct fragment *f);
bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
diff --git a/livetree.c b/livetree.c
index a54009c..3ff4e14 100644
--- a/livetree.c
+++ b/livetree.c
@@ -567,6 +567,12 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
return dti;
}
+/* Check whether a reference is relative to a label, e.g. &{label/node} */
+bool is_ref_relative(const char *ref)
+{
+ return ref[0] != '/' && strchr(&ref[1], '/');
+}
+
/*
* Delete the node which a /delete-node/ fragment refers to
*
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (5 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 06/15] dtc: Move is_ref_relative() to livetree.c Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-05 9:07 ` Herve Codina
2026-11-04 21:17 ` [PATCH v2 08/15] dtc: Merge &{label} in a plugin as with &label Simon Glass
` (7 subsequent siblings)
14 siblings, 1 reply; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The grammar action for a top-level &label { ... } holds its merge logic,
including making an overlay fragment in a plugin when the label is not
found.
Move it into a new merge_ref(), passing it a struct fragment for the
reference and node.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Rename merge_label_ref() to merge_ref()
- Build the fragment in one step, with build_fragment_ref()
- Return true on success from merge_ref()
- Check the fragment type with assert()
dtc-parser.y | 23 +++++++++--------------
dtc.h | 1 +
livetree.c | 26 ++++++++++++++++++++++++++
3 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 9b9b6db..c58a66d 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -214,21 +214,16 @@ devicetree:
}
| devicetree DT_LABEL_REF nodedef
{
- struct node *target = get_node_by_ref($1, $2);
+ struct fragment *f;
- if (target) {
- merge_nodes(target, $3);
- } else {
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if ($<flags>-1 & DTSF_PLUGIN)
- add_orphan_node($1, $3, $2);
- else
- ERROR(&@2, "Label or path %s not found", $2);
- }
+ f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
+ /*
+ * We rely on the rule being always:
+ * versioninfo plugindecl memreserves devicetree
+ * so $-1 is what we want (plugindecl)
+ */
+ if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
+ treesource_error = true;
$$ = $1;
}
| devicetree DT_DEL_NODE dt_ref ';'
diff --git a/dtc.h b/dtc.h
index 8c4d7bf..9b4389b 100644
--- a/dtc.h
+++ b/dtc.h
@@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
bool is_ref_relative(const char *ref);
+bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
bool merge_delete_node(struct node *dt, struct fragment *f);
bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
diff --git a/livetree.c b/livetree.c
index 3ff4e14..179866d 100644
--- a/livetree.c
+++ b/livetree.c
@@ -573,6 +573,32 @@ bool is_ref_relative(const char *ref)
return ref[0] != '/' && strchr(&ref[1], '/');
}
+/*
+ * Merge a fragment which refers to a label into the node with that label. In
+ * a plugin, a label which is not found becomes an overlay fragment.
+ *
+ * Returns true on success, false on an error, which has been reported.
+ */
+bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
+{
+ struct node *target = get_node_by_ref(dt, f->ref);
+
+ assert(f->type == FRAGMENT_NODE);
+ if (target) {
+ merge_nodes(target, f->node);
+ return true;
+ }
+ if (!plugin) {
+ srcpos_error(f->ref_srcpos, "Error",
+ "Label or path %s not found", f->ref);
+ return false;
+ }
+ /* The label may be in the base tree, so add a fragment@N node for it */
+ add_orphan_node(dt, f->node, f->ref);
+
+ return true;
+}
+
/*
* Delete the node which a /delete-node/ fragment refers to
*
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-04 21:17 ` [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar Simon Glass
@ 2026-11-05 9:07 ` Herve Codina
2026-11-05 21:49 ` Simon Glass
0 siblings, 1 reply; 22+ messages in thread
From: Herve Codina @ 2026-11-05 9:07 UTC (permalink / raw)
To: Simon Glass; +Cc: Devicetree Compiler, David Gibson
Hi David, Simon,
On Wed, 4 Nov 2026 14:17:46 -0700
Simon Glass <sjg@chromium.org> wrote:
> The grammar action for a top-level &label { ... } holds its merge logic,
> including making an overlay fragment in a plugin when the label is not
> found.
>
> Move it into a new merge_ref(), passing it a struct fragment for the
> reference and node.
>
> The output does not change.
>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
> Changes in v2:
> - Rename merge_label_ref() to merge_ref()
> - Build the fragment in one step, with build_fragment_ref()
> - Return true on success from merge_ref()
> - Check the fragment type with assert()
>
> dtc-parser.y | 23 +++++++++--------------
> dtc.h | 1 +
> livetree.c | 26 ++++++++++++++++++++++++++
> 3 files changed, 36 insertions(+), 14 deletions(-)
>
> diff --git a/dtc-parser.y b/dtc-parser.y
> index 9b9b6db..c58a66d 100644
> --- a/dtc-parser.y
> +++ b/dtc-parser.y
> @@ -214,21 +214,16 @@ devicetree:
> }
> | devicetree DT_LABEL_REF nodedef
> {
> - struct node *target = get_node_by_ref($1, $2);
> + struct fragment *f;
>
> - if (target) {
> - merge_nodes(target, $3);
> - } else {
> - /*
> - * We rely on the rule being always:
> - * versioninfo plugindecl memreserves devicetree
> - * so $-1 is what we want (plugindecl)
> - */
> - if ($<flags>-1 & DTSF_PLUGIN)
> - add_orphan_node($1, $3, $2);
> - else
> - ERROR(&@2, "Label or path %s not found", $2);
> - }
> + f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
> + /*
> + * We rely on the rule being always:
> + * versioninfo plugindecl memreserves devicetree
> + * so $-1 is what we want (plugindecl)
> + */
> + if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
> + treesource_error = true;
> $$ = $1;
> }
> | devicetree DT_DEL_NODE dt_ref ';'
> diff --git a/dtc.h b/dtc.h
> index 8c4d7bf..9b4389b 100644
> --- a/dtc.h
> +++ b/dtc.h
> @@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
> struct fragment *append_fragment(struct fragment *list, struct fragment *new);
> void free_fragment(struct fragment *f);
> bool is_ref_relative(const char *ref);
> +bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
> bool merge_delete_node(struct node *dt, struct fragment *f);
> bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
>
> diff --git a/livetree.c b/livetree.c
> index 3ff4e14..179866d 100644
> --- a/livetree.c
> +++ b/livetree.c
> @@ -573,6 +573,32 @@ bool is_ref_relative(const char *ref)
> return ref[0] != '/' && strchr(&ref[1], '/');
> }
>
> +/*
> + * Merge a fragment which refers to a label into the node with that label. In
> + * a plugin, a label which is not found becomes an overlay fragment.
> + *
> + * Returns true on success, false on an error, which has been reported.
> + */
> +bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
> +{
> + struct node *target = get_node_by_ref(dt, f->ref);
> +
> + assert(f->type == FRAGMENT_NODE);
> + if (target) {
> + merge_nodes(target, f->node);
> + return true;
> + }
> + if (!plugin) {
> + srcpos_error(f->ref_srcpos, "Error",
> + "Label or path %s not found", f->ref);
> + return false;
> + }
> + /* The label may be in the base tree, so add a fragment@N node for it */
> + add_orphan_node(dt, f->node, f->ref);
> +
> + return true;
> +}
> +
Even if this function is slightly modified in later patches, I think it mixes
two different operations:
- Merging fragments
- Converting fragments in overlay output format.
IHMO, those two operations should be fully decoupled.
merge_ref() or any other operation whose goal is to merge a fragment should
only merge fragment either in the existing root node or in a previous fragment
available in the fragment list.
add_orphan_node() should not be called from a merge_*() function related to
fragments.
After merging all fragment we should have a root node and/or a list of fragment
reduced to unmerged ones.
Base on that, the overlay conversion can be performed using a dedicated
function, out of merge_*() functions.
Something like this:
---- 8< ----
void convert_to_overlay_output_format(struct dt_info *dti)
{
struct fragment *f;
if (!dti->dt) {
/* The root node is not present. Create an empty one */
dti->dt = ...;
}
/*
* The fragment list have been previously merged.
* Available fragment left in the list are fragments
* that cannot be merged and so those fragments are
* orphan nodes.
*
* Convert those orphan nodes to the /fragment@N format overlay
* format.
*/
for (f = dti->fragments; f; f = next) {
add_orphan_node(dti->dt, f->node, f->ref);
/* Add __fixups__ and __local_fixups__ nodes ? */
todo();
}
in main():
/* Parse input */
dti = dt_from_*();
checks_pre_fragment_merge(dti);
merge_fragments(dti); /* <--- Fragments are merged.
* Remaining items in the fragment list, if
* any are Orphan nodes (i.e. fragments that
* couldn't be merged).
*/
checks_post_fragment_merge(dti); <--- Orphan ok for overlays but ko for !overlay dts
/* Convert outputs */
if (dti->dtsflags & DTSF_PLUGIN) {
convert_to_overlay_output_format(dti);
checks_overlay_format(dti); /* <--- Not sure it is needed */
}
/* Generate output */
dt_to_*();
}
--- 8< ---
Best regards,
Hervé
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-05 9:07 ` Herve Codina
@ 2026-11-05 21:49 ` Simon Glass
2026-11-06 6:56 ` Herve Codina
0 siblings, 1 reply; 22+ messages in thread
From: Simon Glass @ 2026-11-05 21:49 UTC (permalink / raw)
To: Herve Codina; +Cc: Devicetree Compiler, David Gibson
Hi Herve,
On Thu, 5 Nov 2026 at 02:07, Herve Codina <herve.codina@bootlin.com> wrote:
>
> Hi David, Simon,
>
> On Wed, 4 Nov 2026 14:17:46 -0700
> Simon Glass <sjg@chromium.org> wrote:
>
> > The grammar action for a top-level &label { ... } holds its merge logic,
> > including making an overlay fragment in a plugin when the label is not
> > found.
> >
> > Move it into a new merge_ref(), passing it a struct fragment for the
> > reference and node.
> >
> > The output does not change.
> >
> > Assisted-by: Claude:claude-opus-5-5
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> >
> > Changes in v2:
> > - Rename merge_label_ref() to merge_ref()
> > - Build the fragment in one step, with build_fragment_ref()
> > - Return true on success from merge_ref()
> > - Check the fragment type with assert()
> >
> > dtc-parser.y | 23 +++++++++--------------
> > dtc.h | 1 +
> > livetree.c | 26 ++++++++++++++++++++++++++
> > 3 files changed, 36 insertions(+), 14 deletions(-)
> >
> > diff --git a/dtc-parser.y b/dtc-parser.y
> > index 9b9b6db..c58a66d 100644
> > --- a/dtc-parser.y
> > +++ b/dtc-parser.y
> > @@ -214,21 +214,16 @@ devicetree:
> > }
> > | devicetree DT_LABEL_REF nodedef
> > {
> > - struct node *target = get_node_by_ref($1, $2);
> > + struct fragment *f;
> >
> > - if (target) {
> > - merge_nodes(target, $3);
> > - } else {
> > - /*
> > - * We rely on the rule being always:
> > - * versioninfo plugindecl memreserves devicetree
> > - * so $-1 is what we want (plugindecl)
> > - */
> > - if ($<flags>-1 & DTSF_PLUGIN)
> > - add_orphan_node($1, $3, $2);
> > - else
> > - ERROR(&@2, "Label or path %s not found", $2);
> > - }
> > + f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
> > + /*
> > + * We rely on the rule being always:
> > + * versioninfo plugindecl memreserves devicetree
> > + * so $-1 is what we want (plugindecl)
> > + */
> > + if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
> > + treesource_error = true;
> > $$ = $1;
> > }
> > | devicetree DT_DEL_NODE dt_ref ';'
> > diff --git a/dtc.h b/dtc.h
> > index 8c4d7bf..9b4389b 100644
> > --- a/dtc.h
> > +++ b/dtc.h
> > @@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
> > struct fragment *append_fragment(struct fragment *list, struct fragment *new);
> > void free_fragment(struct fragment *f);
> > bool is_ref_relative(const char *ref);
> > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
> > bool merge_delete_node(struct node *dt, struct fragment *f);
> > bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
> >
> > diff --git a/livetree.c b/livetree.c
> > index 3ff4e14..179866d 100644
> > --- a/livetree.c
> > +++ b/livetree.c
> > @@ -573,6 +573,32 @@ bool is_ref_relative(const char *ref)
> > return ref[0] != '/' && strchr(&ref[1], '/');
> > }
> >
> > +/*
> > + * Merge a fragment which refers to a label into the node with that label. In
> > + * a plugin, a label which is not found becomes an overlay fragment.
> > + *
> > + * Returns true on success, false on an error, which has been reported.
> > + */
> > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
> > +{
> > + struct node *target = get_node_by_ref(dt, f->ref);
> > +
> > + assert(f->type == FRAGMENT_NODE);
> > + if (target) {
> > + merge_nodes(target, f->node);
> > + return true;
> > + }
> > + if (!plugin) {
> > + srcpos_error(f->ref_srcpos, "Error",
> > + "Label or path %s not found", f->ref);
> > + return false;
> > + }
> > + /* The label may be in the base tree, so add a fragment@N node for it */
> > + add_orphan_node(dt, f->node, f->ref);
> > +
> > + return true;
> > +}
> > +
>
> Even if this function is slightly modified in later patches, I think it mixes
> two different operations:
> - Merging fragments
> - Converting fragments in overlay output format.
>
> IHMO, those two operations should be fully decoupled.
>
> merge_ref() or any other operation whose goal is to merge a fragment should
> only merge fragment either in the existing root node or in a previous fragment
> available in the fragment list.
>
> add_orphan_node() should not be called from a merge_*() function related to
> fragments.
>
> After merging all fragment we should have a root node and/or a list of fragment
> reduced to unmerged ones.
>
> Base on that, the overlay conversion can be performed using a dedicated
> function, out of merge_*() functions.
>
> Something like this:
> ---- 8< ----
> void convert_to_overlay_output_format(struct dt_info *dti)
> {
> struct fragment *f;
>
> if (!dti->dt) {
> /* The root node is not present. Create an empty one */
> dti->dt = ...;
> }
>
> /*
> * The fragment list have been previously merged.
> * Available fragment left in the list are fragments
> * that cannot be merged and so those fragments are
> * orphan nodes.
> *
> * Convert those orphan nodes to the /fragment@N format overlay
> * format.
> */
> for (f = dti->fragments; f; f = next) {
> add_orphan_node(dti->dt, f->node, f->ref);
>
> /* Add __fixups__ and __local_fixups__ nodes ? */
> todo();
> }
>
> in main():
>
> /* Parse input */
> dti = dt_from_*();
>
> checks_pre_fragment_merge(dti);
>
> merge_fragments(dti); /* <--- Fragments are merged.
> * Remaining items in the fragment list, if
> * any are Orphan nodes (i.e. fragments that
> * couldn't be merged).
> */
>
> checks_post_fragment_merge(dti); <--- Orphan ok for overlays but ko for !overlay dts
>
> /* Convert outputs */
> if (dti->dtsflags & DTSF_PLUGIN) {
> convert_to_overlay_output_format(dti);
> checks_overlay_format(dti); /* <--- Not sure it is needed */
> }
>
> /* Generate output */
> dt_to_*();
> }
> --- 8< ---
I tend to agree - we are heading in this direction.
But this series is meant to keep the output the same (apart from the
&{label} case), so I would prefer to do the split as a follow-up. I
just filed an issue for the overall effort here:
https://github.com/dgibson/dtc/issues/201
What do you think?
Regards,
Simon
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-05 21:49 ` Simon Glass
@ 2026-11-06 6:56 ` Herve Codina
2026-11-06 13:05 ` Simon Glass
0 siblings, 1 reply; 22+ messages in thread
From: Herve Codina @ 2026-11-06 6:56 UTC (permalink / raw)
To: Simon Glass; +Cc: Devicetree Compiler, David Gibson
Hi Simon,
On Thu, 5 Nov 2026 14:49:45 -0700
Simon Glass <sjg@chromium.org> wrote:
> Hi Herve,
>
> On Thu, 5 Nov 2026 at 02:07, Herve Codina <herve.codina@bootlin.com> wrote:
> >
> > Hi David, Simon,
> >
> > On Wed, 4 Nov 2026 14:17:46 -0700
> > Simon Glass <sjg@chromium.org> wrote:
> >
> > > The grammar action for a top-level &label { ... } holds its merge logic,
> > > including making an overlay fragment in a plugin when the label is not
> > > found.
> > >
> > > Move it into a new merge_ref(), passing it a struct fragment for the
> > > reference and node.
> > >
> > > The output does not change.
> > >
> > > Assisted-by: Claude:claude-opus-5-5
> > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > ---
> > >
> > > Changes in v2:
> > > - Rename merge_label_ref() to merge_ref()
> > > - Build the fragment in one step, with build_fragment_ref()
> > > - Return true on success from merge_ref()
> > > - Check the fragment type with assert()
> > >
> > > dtc-parser.y | 23 +++++++++--------------
> > > dtc.h | 1 +
> > > livetree.c | 26 ++++++++++++++++++++++++++
> > > 3 files changed, 36 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/dtc-parser.y b/dtc-parser.y
> > > index 9b9b6db..c58a66d 100644
> > > --- a/dtc-parser.y
> > > +++ b/dtc-parser.y
> > > @@ -214,21 +214,16 @@ devicetree:
> > > }
> > > | devicetree DT_LABEL_REF nodedef
> > > {
> > > - struct node *target = get_node_by_ref($1, $2);
> > > + struct fragment *f;
> > >
> > > - if (target) {
> > > - merge_nodes(target, $3);
> > > - } else {
> > > - /*
> > > - * We rely on the rule being always:
> > > - * versioninfo plugindecl memreserves devicetree
> > > - * so $-1 is what we want (plugindecl)
> > > - */
> > > - if ($<flags>-1 & DTSF_PLUGIN)
> > > - add_orphan_node($1, $3, $2);
> > > - else
> > > - ERROR(&@2, "Label or path %s not found", $2);
> > > - }
> > > + f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
> > > + /*
> > > + * We rely on the rule being always:
> > > + * versioninfo plugindecl memreserves devicetree
> > > + * so $-1 is what we want (plugindecl)
> > > + */
> > > + if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
> > > + treesource_error = true;
> > > $$ = $1;
> > > }
> > > | devicetree DT_DEL_NODE dt_ref ';'
> > > diff --git a/dtc.h b/dtc.h
> > > index 8c4d7bf..9b4389b 100644
> > > --- a/dtc.h
> > > +++ b/dtc.h
> > > @@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
> > > struct fragment *append_fragment(struct fragment *list, struct fragment *new);
> > > void free_fragment(struct fragment *f);
> > > bool is_ref_relative(const char *ref);
> > > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
> > > bool merge_delete_node(struct node *dt, struct fragment *f);
> > > bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
> > >
> > > diff --git a/livetree.c b/livetree.c
> > > index 3ff4e14..179866d 100644
> > > --- a/livetree.c
> > > +++ b/livetree.c
> > > @@ -573,6 +573,32 @@ bool is_ref_relative(const char *ref)
> > > return ref[0] != '/' && strchr(&ref[1], '/');
> > > }
> > >
> > > +/*
> > > + * Merge a fragment which refers to a label into the node with that label. In
> > > + * a plugin, a label which is not found becomes an overlay fragment.
> > > + *
> > > + * Returns true on success, false on an error, which has been reported.
> > > + */
> > > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
> > > +{
> > > + struct node *target = get_node_by_ref(dt, f->ref);
> > > +
> > > + assert(f->type == FRAGMENT_NODE);
> > > + if (target) {
> > > + merge_nodes(target, f->node);
> > > + return true;
> > > + }
> > > + if (!plugin) {
> > > + srcpos_error(f->ref_srcpos, "Error",
> > > + "Label or path %s not found", f->ref);
> > > + return false;
> > > + }
> > > + /* The label may be in the base tree, so add a fragment@N node for it */
> > > + add_orphan_node(dt, f->node, f->ref);
> > > +
> > > + return true;
> > > +}
> > > +
> >
> > Even if this function is slightly modified in later patches, I think it mixes
> > two different operations:
> > - Merging fragments
> > - Converting fragments in overlay output format.
> >
> > IHMO, those two operations should be fully decoupled.
> >
> > merge_ref() or any other operation whose goal is to merge a fragment should
> > only merge fragment either in the existing root node or in a previous fragment
> > available in the fragment list.
> >
> > add_orphan_node() should not be called from a merge_*() function related to
> > fragments.
> >
> > After merging all fragment we should have a root node and/or a list of fragment
> > reduced to unmerged ones.
> >
> > Base on that, the overlay conversion can be performed using a dedicated
> > function, out of merge_*() functions.
> >
> > Something like this:
> > ---- 8< ----
> > void convert_to_overlay_output_format(struct dt_info *dti)
> > {
> > struct fragment *f;
> >
> > if (!dti->dt) {
> > /* The root node is not present. Create an empty one */
> > dti->dt = ...;
> > }
> >
> > /*
> > * The fragment list have been previously merged.
> > * Available fragment left in the list are fragments
> > * that cannot be merged and so those fragments are
> > * orphan nodes.
> > *
> > * Convert those orphan nodes to the /fragment@N format overlay
> > * format.
> > */
> > for (f = dti->fragments; f; f = next) {
> > add_orphan_node(dti->dt, f->node, f->ref);
> >
> > /* Add __fixups__ and __local_fixups__ nodes ? */
> > todo();
> > }
> >
> > in main():
> >
> > /* Parse input */
> > dti = dt_from_*();
> >
> > checks_pre_fragment_merge(dti);
> >
> > merge_fragments(dti); /* <--- Fragments are merged.
> > * Remaining items in the fragment list, if
> > * any are Orphan nodes (i.e. fragments that
> > * couldn't be merged).
> > */
> >
> > checks_post_fragment_merge(dti); <--- Orphan ok for overlays but ko for !overlay dts
> >
> > /* Convert outputs */
> > if (dti->dtsflags & DTSF_PLUGIN) {
> > convert_to_overlay_output_format(dti);
> > checks_overlay_format(dti); /* <--- Not sure it is needed */
> > }
> >
> > /* Generate output */
> > dt_to_*();
> > }
> > --- 8< ---
>
> I tend to agree - we are heading in this direction.
>
> But this series is meant to keep the output the same (apart from the
> &{label} case), so I would prefer to do the split as a follow-up. I
> just filed an issue for the overall effort here:
I don't see why splitting merge and "overlay format conversion" changes the
output.
This series could split these operations in two distinct functions.
>
> https://github.com/dgibson/dtc/issues/201
>
> What do you think?
>
> Regards,
> Simon
Best regards,
Hervé
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-06 6:56 ` Herve Codina
@ 2026-11-06 13:05 ` Simon Glass
2026-11-06 15:55 ` Herve Codina
0 siblings, 1 reply; 22+ messages in thread
From: Simon Glass @ 2026-11-06 13:05 UTC (permalink / raw)
To: Herve Codina; +Cc: Devicetree Compiler, David Gibson
Hi Herve,
On Thu, 5 Nov 2026 at 23:56, Herve Codina <herve.codina@bootlin.com> wrote:
>
> Hi Simon,
>
> On Thu, 5 Nov 2026 14:49:45 -0700
> Simon Glass <sjg@chromium.org> wrote:
>
> > Hi Herve,
> >
> > On Thu, 5 Nov 2026 at 02:07, Herve Codina <herve.codina@bootlin.com> wrote:
> > >
> > > Hi David, Simon,
> > >
> > > On Wed, 4 Nov 2026 14:17:46 -0700
> > > Simon Glass <sjg@chromium.org> wrote:
> > >
> > > > The grammar action for a top-level &label { ... } holds its merge logic,
> > > > including making an overlay fragment in a plugin when the label is not
> > > > found.
> > > >
> > > > Move it into a new merge_ref(), passing it a struct fragment for the
> > > > reference and node.
> > > >
> > > > The output does not change.
> > > >
> > > > Assisted-by: Claude:claude-opus-5-5
> > > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > > ---
> > > >
> > > > Changes in v2:
> > > > - Rename merge_label_ref() to merge_ref()
> > > > - Build the fragment in one step, with build_fragment_ref()
> > > > - Return true on success from merge_ref()
> > > > - Check the fragment type with assert()
> > > >
> > > > dtc-parser.y | 23 +++++++++--------------
> > > > dtc.h | 1 +
> > > > livetree.c | 26 ++++++++++++++++++++++++++
> > > > 3 files changed, 36 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/dtc-parser.y b/dtc-parser.y
> > > > index 9b9b6db..c58a66d 100644
> > > > --- a/dtc-parser.y
> > > > +++ b/dtc-parser.y
> > > > @@ -214,21 +214,16 @@ devicetree:
> > > > }
> > > > | devicetree DT_LABEL_REF nodedef
> > > > {
> > > > - struct node *target = get_node_by_ref($1, $2);
> > > > + struct fragment *f;
> > > >
> > > > - if (target) {
> > > > - merge_nodes(target, $3);
> > > > - } else {
> > > > - /*
> > > > - * We rely on the rule being always:
> > > > - * versioninfo plugindecl memreserves devicetree
> > > > - * so $-1 is what we want (plugindecl)
> > > > - */
> > > > - if ($<flags>-1 & DTSF_PLUGIN)
> > > > - add_orphan_node($1, $3, $2);
> > > > - else
> > > > - ERROR(&@2, "Label or path %s not found", $2);
> > > > - }
> > > > + f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
> > > > + /*
> > > > + * We rely on the rule being always:
> > > > + * versioninfo plugindecl memreserves devicetree
> > > > + * so $-1 is what we want (plugindecl)
> > > > + */
> > > > + if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
> > > > + treesource_error = true;
> > > > $$ = $1;
> > > > }
> > > > | devicetree DT_DEL_NODE dt_ref ';'
> > > > diff --git a/dtc.h b/dtc.h
> > > > index 8c4d7bf..9b4389b 100644
> > > > --- a/dtc.h
> > > > +++ b/dtc.h
> > > > @@ -373,6 +373,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
> > > > struct fragment *append_fragment(struct fragment *list, struct fragment *new);
> > > > void free_fragment(struct fragment *f);
> > > > bool is_ref_relative(const char *ref);
> > > > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
> > > > bool merge_delete_node(struct node *dt, struct fragment *f);
> > > > bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
> > > >
> > > > diff --git a/livetree.c b/livetree.c
> > > > index 3ff4e14..179866d 100644
> > > > --- a/livetree.c
> > > > +++ b/livetree.c
> > > > @@ -573,6 +573,32 @@ bool is_ref_relative(const char *ref)
> > > > return ref[0] != '/' && strchr(&ref[1], '/');
> > > > }
> > > >
> > > > +/*
> > > > + * Merge a fragment which refers to a label into the node with that label. In
> > > > + * a plugin, a label which is not found becomes an overlay fragment.
> > > > + *
> > > > + * Returns true on success, false on an error, which has been reported.
> > > > + */
> > > > +bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
> > > > +{
> > > > + struct node *target = get_node_by_ref(dt, f->ref);
> > > > +
> > > > + assert(f->type == FRAGMENT_NODE);
> > > > + if (target) {
> > > > + merge_nodes(target, f->node);
> > > > + return true;
> > > > + }
> > > > + if (!plugin) {
> > > > + srcpos_error(f->ref_srcpos, "Error",
> > > > + "Label or path %s not found", f->ref);
> > > > + return false;
> > > > + }
> > > > + /* The label may be in the base tree, so add a fragment@N node for it */
> > > > + add_orphan_node(dt, f->node, f->ref);
> > > > +
> > > > + return true;
> > > > +}
> > > > +
> > >
> > > Even if this function is slightly modified in later patches, I think it mixes
> > > two different operations:
> > > - Merging fragments
> > > - Converting fragments in overlay output format.
> > >
> > > IHMO, those two operations should be fully decoupled.
> > >
> > > merge_ref() or any other operation whose goal is to merge a fragment should
> > > only merge fragment either in the existing root node or in a previous fragment
> > > available in the fragment list.
> > >
> > > add_orphan_node() should not be called from a merge_*() function related to
> > > fragments.
> > >
> > > After merging all fragment we should have a root node and/or a list of fragment
> > > reduced to unmerged ones.
> > >
> > > Base on that, the overlay conversion can be performed using a dedicated
> > > function, out of merge_*() functions.
> > >
> > > Something like this:
> > > ---- 8< ----
> > > void convert_to_overlay_output_format(struct dt_info *dti)
> > > {
> > > struct fragment *f;
> > >
> > > if (!dti->dt) {
> > > /* The root node is not present. Create an empty one */
> > > dti->dt = ...;
> > > }
> > >
> > > /*
> > > * The fragment list have been previously merged.
> > > * Available fragment left in the list are fragments
> > > * that cannot be merged and so those fragments are
> > > * orphan nodes.
> > > *
> > > * Convert those orphan nodes to the /fragment@N format overlay
> > > * format.
> > > */
> > > for (f = dti->fragments; f; f = next) {
> > > add_orphan_node(dti->dt, f->node, f->ref);
> > >
> > > /* Add __fixups__ and __local_fixups__ nodes ? */
> > > todo();
> > > }
> > >
> > > in main():
> > >
> > > /* Parse input */
> > > dti = dt_from_*();
> > >
> > > checks_pre_fragment_merge(dti);
> > >
> > > merge_fragments(dti); /* <--- Fragments are merged.
> > > * Remaining items in the fragment list, if
> > > * any are Orphan nodes (i.e. fragments that
> > > * couldn't be merged).
> > > */
> > >
> > > checks_post_fragment_merge(dti); <--- Orphan ok for overlays but ko for !overlay dts
> > >
> > > /* Convert outputs */
> > > if (dti->dtsflags & DTSF_PLUGIN) {
> > > convert_to_overlay_output_format(dti);
> > > checks_overlay_format(dti); /* <--- Not sure it is needed */
> > > }
> > >
> > > /* Generate output */
> > > dt_to_*();
> > > }
> > > --- 8< ---
> >
> > I tend to agree - we are heading in this direction.
> >
> > But this series is meant to keep the output the same (apart from the
> > &{label} case), so I would prefer to do the split as a follow-up. I
> > just filed an issue for the overall effort here:
>
> I don't see why splitting merge and "overlay format conversion" changes the
> output.
>
> This series could split these operations in two distinct functions.
It's only very minor. For example:
/plugin/;
&foo { mine: x { a; }; };
/ { bar { }; };
&mine { b; };
&foo { c; };
This series will have bar between fragment@0 and fragment@1, but with
this change it comes before both fragments. I think that is fine to do
that, just that to my mind it fits better with step 2b of David’s
plan, where each fragment is wrapped in fragment@N after the merge.
There are a couple of other corner cases too, which I'm looking at.
Anyway I've got a patch for your change and I'd like to put it at the
end of the v3 series, since I am trying to move code largely unchanged
out of the parser, avoiding behaviour changes as much as possible in
this series. Does that sound OK?
Regards,
SImon
>
> >
> > https://github.com/dgibson/dtc/issues/201
> >
> > What do you think?
[..]
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-06 13:05 ` Simon Glass
@ 2026-11-06 15:55 ` Herve Codina
2026-11-06 17:30 ` Simon Glass
0 siblings, 1 reply; 22+ messages in thread
From: Herve Codina @ 2026-11-06 15:55 UTC (permalink / raw)
To: Simon Glass; +Cc: Devicetree Compiler, David Gibson
Hi Simon,
On Fri, 6 Nov 2026 06:05:52 -0700
Simon Glass <sjg@chromium.org> wrote:
...
>
> It's only very minor. For example:
>
> /plugin/;
> &foo { mine: x { a; }; };
> / { bar { }; };
> &mine { b; };
> &foo { c; };
>
> This series will have bar between fragment@0 and fragment@1, but with
> this change it comes before both fragments. I think that is fine to do
> that, just that to my mind it fits better with step 2b of David’s
> plan, where each fragment is wrapped in fragment@N after the merge.
> There are a couple of other corner cases too, which I'm looking at.
I suppose fragment@0 and fragment@1 are the overlay output dts. If so, yes,
I think also it is fine.
Still need to be acked by David.
Otherwise the solution could be to have only fragments after parsing (the
root node is a fragment).
merge_fragments() merge fragments in previous one.
The fragment list in dt_info contains orphans + the root node.
The fragments order shouldn't be modified in that case.
Then overlay_convert_output_format() will have to deal with that.
Anyway, I don't think that adding this complexity is necessary to handle
this "bar in middle" use case.
In the end, nodes are not supposed to be ordered. Anything depending on
node ordering is not correct.
IHMO, internal fragments are obviously in an ordered list (same order as
input file) but nodes in the output file don't have any order requirements
except if '--sort' option is used but it should be already ok if you perform
the sort after the overlay_convert_output_format() call.
>
> Anyway I've got a patch for your change and I'd like to put it at the
> end of the v3 series, since I am trying to move code largely unchanged
> out of the parser, avoiding behaviour changes as much as possible in
> this series. Does that sound OK?
No problem. Having it at the end of the series is ok on my side.
Best regards,
Hervé
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar
2026-11-06 15:55 ` Herve Codina
@ 2026-11-06 17:30 ` Simon Glass
0 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-06 17:30 UTC (permalink / raw)
To: Herve Codina; +Cc: Devicetree Compiler, David Gibson
Hi Herve,
On Fri, 6 Nov 2026 at 08:55, Herve Codina <herve.codina@bootlin.com> wrote:
>
> Hi Simon,
>
> On Fri, 6 Nov 2026 06:05:52 -0700
> Simon Glass <sjg@chromium.org> wrote:
>
> ...
>
> >
> > It's only very minor. For example:
> >
> > /plugin/;
> > &foo { mine: x { a; }; };
> > / { bar { }; };
> > &mine { b; };
> > &foo { c; };
> >
> > This series will have bar between fragment@0 and fragment@1, but with
> > this change it comes before both fragments. I think that is fine to do
> > that, just that to my mind it fits better with step 2b of David’s
> > plan, where each fragment is wrapped in fragment@N after the merge.
> > There are a couple of other corner cases too, which I'm looking at.
>
> I suppose fragment@0 and fragment@1 are the overlay output dts. If so, yes,
> I think also it is fine.
OK good
>
> Still need to be acked by David.
>
> Otherwise the solution could be to have only fragments after parsing (the
> root node is a fragment).
>
> merge_fragments() merge fragments in previous one.
>
> The fragment list in dt_info contains orphans + the root node.
> The fragments order shouldn't be modified in that case.
>
> Then overlay_convert_output_format() will have to deal with that.
>
> Anyway, I don't think that adding this complexity is necessary to handle
> this "bar in middle" use case.
>
> In the end, nodes are not supposed to be ordered. Anything depending on
> node ordering is not correct.
Agreed it doesn't seem worth it. Ordering isn't in the spec, after
all. Also, none of the Linux files show any difference with this
change.
>
> IHMO, internal fragments are obviously in an ordered list (same order as
> input file) but nodes in the output file don't have any order requirements
> except if '--sort' option is used but it should be already ok if you perform
> the sort after the overlay_convert_output_format() call.
Yes. From the point of view of humans I think it is nice to keep the
order stable if we can, so the source code matches the dtb output. Of
course this doesn't really apply when you are talking about overlays
and fragments.
>
> >
> > Anyway I've got a patch for your change and I'd like to put it at the
> > end of the v3 series, since I am trying to move code largely unchanged
> > out of the parser, avoiding behaviour changes as much as possible in
> > this series. Does that sound OK?
>
> No problem. Having it at the end of the series is ok on my side.
OK I'll do that. I probably should have split the series after the v1
feedback, but I'll send v3 as a monolithic series and we'll see how we
go.
Regards,
Simon
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 08/15] dtc: Merge &{label} in a plugin as with &label
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (6 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 07/15] dtc: Move label-reference handling out of the grammar Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 09/15] dtc: Move path-reference handling out of the grammar Simon Glass
` (6 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
In a plugin, &label { ... } is merged into the labelled node if the
label is in the plugin, as of commit 0fcffda15e9f ("Merge nodes with
local target label references"). But &{label} { ... } always becomes a
separate fragment@N node, since anything in braces is lexed as a path
reference. This adds a fragment which is not needed, which that commit
aims to avoid, and fails with FDT_ERR_NOTFOUND if the label is not
within a fragment.
Treat a reference as a path only if it starts with '/', so that both
forms are merged into the node. This changes the output for such a file.
Applying an overlay which already works gives the same result, apart
from the order of the properties and that a later &label { ... } now
overrides an earlier &{label} { ... }, as the source order suggests.
Other references are not affected. An absolute path still becomes a
fragment@N node, as does a label which is not in the plugin, since it
may be in the base tree.
This also means that dtc does not need to record whether a reference is
written in braces. The merge code, which is about to move out of the
grammar, can tell a path from a label by looking at the reference.
Update the merge-label-braces.dts and merge-label-order.dts tests to
match.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Add new patch for &{label} in a plugin
dtc-parser.y | 10 +++++++---
tests/merge-label-braces.dts | 2 +-
tests/merge-label-order.dts | 2 +-
tests/run_tests.sh | 10 +++++-----
4 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index c58a66d..3614d71 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -193,6 +193,8 @@ devicetree:
}
| devicetree DT_PATH_REF nodedef
{
+ struct node *target = get_node_by_ref($1, $2);
+
/*
* We rely on the rule being always:
* versioninfo plugindecl memreserves devicetree
@@ -201,10 +203,12 @@ devicetree:
if ($<flags>-1 & DTSF_PLUGIN) {
if (is_ref_relative($2))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
- add_orphan_node($1, $3, $2);
+ /* A path is in the base tree and a label may be */
+ if ($2[0] == '/' || !target)
+ add_orphan_node($1, $3, $2);
+ else
+ merge_nodes(target, $3);
} else {
- struct node *target = get_node_by_ref($1, $2);
-
if (target)
merge_nodes(target, $3);
else
diff --git a/tests/merge-label-braces.dts b/tests/merge-label-braces.dts
index d5f8d0f..569e7f8 100644
--- a/tests/merge-label-braces.dts
+++ b/tests/merge-label-braces.dts
@@ -6,7 +6,7 @@
};
};
-/* This becomes an overlay fragment, even though the label is in the plugin */
+/* A label in the plugin itself is merged, however it is written */
&{label} {
prop;
};
diff --git a/tests/merge-label-order.dts b/tests/merge-label-order.dts
index f7923a0..40fa668 100644
--- a/tests/merge-label-order.dts
+++ b/tests/merge-label-order.dts
@@ -7,7 +7,7 @@
};
};
-/* The fragment for &{label} is applied last, so "second" wins */
+/* The last definition wins */
&{label} {
p = "second";
};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 7beb6ae..4661216 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -812,17 +812,17 @@ dtc_tests () {
run_fdtget_test "/node" merge-labelled-plugin.test.dtb /__symbols__ newlabel
run_fdtget_test "value" merge-labelled-plugin.test.dtb /node prop
- # In a plugin, &{label} becomes an overlay fragment, even if it is found
+ # In a plugin, &{label} is the same as &label
run_dtc_test -I dts -O dtb -o merge-label-braces.test.dtb \
"$SRCDIR/merge-label-braces.dts"
- run_fdtget_test "" merge-label-braces.test.dtb /fragment@0/__overlay__ prop
- run_fdtget_test "node\nfragment@0\n__local_fixups__" merge-label-braces.test.dtb -l /
+ run_fdtget_test "" merge-label-braces.test.dtb /node prop
+ run_fdtget_test "node" merge-label-braces.test.dtb -l /
- # The fragment for &{label} is applied after any local merges
+ # The last definition wins, with no fragment for &{label}
run_dtc_test -I dts -O dtb -o merge-label-order.test.dtb \
"$SRCDIR/merge-label-order.dts"
run_fdtget_test "third" merge-label-order.test.dtb /fragment@0/__overlay__/foo p
- run_fdtget_test "second" merge-label-order.test.dtb /fragment@1/__overlay__ p
+ run_fdtget_test "fragment@0\n__fixups__" merge-label-order.test.dtb -l /
# In a plugin, / { ... } and &{/} { ... } are not the same thing
run_dtc_test -I dts -O dtb -o merge-root-plugin.test.dtb \
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 09/15] dtc: Move path-reference handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (7 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 08/15] dtc: Merge &{label} in a plugin as with &label Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 10/15] dtc: Move labelled-reference " Simon Glass
` (5 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The grammar action for a top-level &{/path} { ... } holds its merge
logic, including making an overlay fragment in a plugin.
Move it into merge_ref(), which can tell a path from a label by looking
at the reference, and use a single rule for both kinds of reference.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Combine merge_path_ref() into merge_ref()
- Combine the rules for label and path references, using dt_ref
dtc-parser.y | 27 +--------------------------
livetree.c | 33 +++++++++++++++++++++++----------
2 files changed, 24 insertions(+), 36 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 3614d71..3ec9254 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -191,32 +191,7 @@ devicetree:
ERROR(&@3, "Label or path %s not found", $3);
$$ = $1;
}
- | devicetree DT_PATH_REF nodedef
- {
- struct node *target = get_node_by_ref($1, $2);
-
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if ($<flags>-1 & DTSF_PLUGIN) {
- if (is_ref_relative($2))
- ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
- /* A path is in the base tree and a label may be */
- if ($2[0] == '/' || !target)
- add_orphan_node($1, $3, $2);
- else
- merge_nodes(target, $3);
- } else {
- if (target)
- merge_nodes(target, $3);
- else
- ERROR(&@2, "Label or path %s not found", $2);
- }
- $$ = $1;
- }
- | devicetree DT_LABEL_REF nodedef
+ | devicetree dt_ref nodedef
{
struct fragment *f;
diff --git a/livetree.c b/livetree.c
index 179866d..2e9a3b2 100644
--- a/livetree.c
+++ b/livetree.c
@@ -574,29 +574,42 @@ bool is_ref_relative(const char *ref)
}
/*
- * Merge a fragment which refers to a label into the node with that label. In
- * a plugin, a label which is not found becomes an overlay fragment.
+ * Merge a fragment which refers to a label or path into the node it refers to.
+ *
+ * In a plugin, a path always refers to the base tree and a label may do so, if
+ * it is not found. In both cases, the fragment becomes an overlay fragment.
*
* Returns true on success, false on an error, which has been reported.
*/
bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
{
- struct node *target = get_node_by_ref(dt, f->ref);
+ struct node *target;
+ bool ok = true;
assert(f->type == FRAGMENT_NODE);
- if (target) {
- merge_nodes(target, f->node);
- return true;
+ if (plugin) {
+ if (is_ref_relative(f->ref)) {
+ srcpos_error(f->ref_srcpos, "Error",
+ "Label-relative reference %s not supported in plugin",
+ f->ref);
+ ok = false;
+ }
+ if (f->ref[0] == '/' || !get_node_by_ref(dt, f->ref)) {
+ /* Add a fragment@N node for the reference */
+ add_orphan_node(dt, f->node, f->ref);
+ return ok;
+ }
}
- if (!plugin) {
+
+ target = get_node_by_ref(dt, f->ref);
+ if (!target) {
srcpos_error(f->ref_srcpos, "Error",
"Label or path %s not found", f->ref);
return false;
}
- /* The label may be in the base tree, so add a fragment@N node for it */
- add_orphan_node(dt, f->node, f->ref);
+ merge_nodes(target, f->node);
- return true;
+ return ok;
}
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 10/15] dtc: Move labelled-reference handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (8 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 09/15] dtc: Move path-reference handling out of the grammar Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 11/15] dtc: Move first-reference " Simon Glass
` (4 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
The grammar action for a top-level label: &ref { ... } holds its merge
logic, which adds the label to the node referred to.
Move it into merge_ref(), passing it a struct fragment with the label.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Combine merge_labelled_ref() into merge_ref()
- Use dt_ref instead of fragment_ref
dtc-parser.y | 13 ++++---------
livetree.c | 14 ++++++++++----
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 3ec9254..8d505a1 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -179,16 +179,11 @@ devicetree:
}
| devicetree DT_LABEL dt_ref nodedef
{
- struct node *target = get_node_by_ref($1, $3);
-
- if (($<flags>-1 & DTSF_PLUGIN) && is_ref_relative($3))
- ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
+ struct fragment *f;
- if (target) {
- add_label(&target->labels, $2);
- merge_nodes(target, $4);
- } else
- ERROR(&@3, "Label or path %s not found", $3);
+ f = build_fragment_ref($2, &@2, $3, &@3, $4);
+ if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
+ treesource_error = true;
$$ = $1;
}
| devicetree dt_ref nodedef
diff --git a/livetree.c b/livetree.c
index 2e9a3b2..7ab9437 100644
--- a/livetree.c
+++ b/livetree.c
@@ -574,10 +574,12 @@ bool is_ref_relative(const char *ref)
}
/*
- * Merge a fragment which refers to a label or path into the node it refers to.
+ * Merge a fragment which refers to a label or path into the node it refers to,
+ * adding any label in the fragment to that node.
*
* In a plugin, a path always refers to the base tree and a label may do so, if
- * it is not found. In both cases, the fragment becomes an overlay fragment.
+ * it is not found. In both cases, the fragment becomes an overlay fragment,
+ * unless it adds a label, which needs a node in the plugin itself.
*
* Returns true on success, false on an error, which has been reported.
*/
@@ -589,12 +591,14 @@ bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
assert(f->type == FRAGMENT_NODE);
if (plugin) {
if (is_ref_relative(f->ref)) {
- srcpos_error(f->ref_srcpos, "Error",
+ srcpos_error(f->label ? f->label_srcpos : f->ref_srcpos,
+ "Error",
"Label-relative reference %s not supported in plugin",
f->ref);
ok = false;
}
- if (f->ref[0] == '/' || !get_node_by_ref(dt, f->ref)) {
+ if (!f->label &&
+ (f->ref[0] == '/' || !get_node_by_ref(dt, f->ref))) {
/* Add a fragment@N node for the reference */
add_orphan_node(dt, f->node, f->ref);
return ok;
@@ -607,6 +611,8 @@ bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
"Label or path %s not found", f->ref);
return false;
}
+ if (f->label)
+ add_label(&target->labels, f->label);
merge_nodes(target, f->node);
return ok;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 11/15] dtc: Move first-reference handling out of the grammar
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (9 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 10/15] dtc: Move labelled-reference " Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 12/15] dtc: Add merge_fragment() to merge any fragment Simon Glass
` (3 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
In a plugin, the first top-level fragment can refer to a label or path
instead of being the root node. The grammar action for this holds its
merge logic, which makes an overlay fragment in a new tree.
Move it into a new merge_first_ref(), passing it a struct fragment for
the reference and node. This is the last user of is_ref_relative() in
the grammar, so make that function static.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Use dt_ref instead of fragment_ref
- Return true on success from merge_first_ref()
dtc-parser.y | 13 +++++--------
dtc.h | 2 +-
livetree.c | 30 +++++++++++++++++++++++++++++-
3 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 8d505a1..abeee0d 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -163,19 +163,16 @@ devicetree:
}
| dt_ref nodedef
{
+ struct fragment *f;
+
+ f = build_fragment_ref(NULL, NULL, $1, &@1, $2);
/*
* We rely on the rule being always:
* versioninfo plugindecl memreserves devicetree
* so $-1 is what we want (plugindecl)
*/
- if (!($<flags>-1 & DTSF_PLUGIN))
- ERROR(&@2, "Label or path %s not found", $1);
- else if (is_ref_relative($1))
- ERROR(&@2, "Label-relative reference %s not supported in plugin", $1);
- $$ = add_orphan_node(
- name_node(build_node(NULL, NULL, NULL),
- ""),
- $2, $1);
+ if (!merge_first_ref(&$$, f, $<flags>-1 & DTSF_PLUGIN))
+ treesource_error = true;
}
| devicetree DT_LABEL dt_ref nodedef
{
diff --git a/dtc.h b/dtc.h
index 9b4389b..1befd5c 100644
--- a/dtc.h
+++ b/dtc.h
@@ -372,7 +372,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct srcpos *ref_srcpos);
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
-bool is_ref_relative(const char *ref);
+bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin);
bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
bool merge_delete_node(struct node *dt, struct fragment *f);
bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
diff --git a/livetree.c b/livetree.c
index 7ab9437..26d39df 100644
--- a/livetree.c
+++ b/livetree.c
@@ -568,11 +568,39 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
}
/* Check whether a reference is relative to a label, e.g. &{label/node} */
-bool is_ref_relative(const char *ref)
+static bool is_ref_relative(const char *ref)
{
return ref[0] != '/' && strchr(&ref[1], '/');
}
+/*
+ * Make the tree at *dtp from a first fragment which refers to a label or path,
+ * rather than being the root node. This is only valid in a plugin, where the
+ * fragment becomes an overlay fragment.
+ *
+ * Returns true on success, false on an error, which has been reported.
+ */
+bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin)
+{
+ bool ok = true;
+
+ if (!plugin) {
+ srcpos_error(f->node->srcpos, "Error",
+ "Label or path %s not found", f->ref);
+ ok = false;
+ } else if (is_ref_relative(f->ref)) {
+ srcpos_error(f->node->srcpos, "Error",
+ "Label-relative reference %s not supported in plugin",
+ f->ref);
+ ok = false;
+ }
+ /* Make a new, empty tree with a fragment@N node for the reference */
+ *dtp = add_orphan_node(name_node(build_node(NULL, NULL, NULL), ""),
+ f->node, f->ref);
+
+ return ok;
+}
+
/*
* Merge a fragment which refers to a label or path into the node it refers to,
* adding any label in the fragment to that node.
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 12/15] dtc: Add merge_fragment() to merge any fragment
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (10 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 11/15] dtc: Move first-reference " Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 13/15] dtc: Add helpers for the errors when merging a fragment Simon Glass
` (2 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
Each grammar action which merges a fragment calls the function for its
own kind of fragment, sets treesource_error if that fails, and finds out
whether the file is a plugin using $<flags>-1
Add merge_fragment() to merge any fragment, choosing the function by the
type of fragment and whether it is the first. Have each action build a
struct fragment and pass it to a new merge_parsed_fragment() helper,
which calls merge_fragment(), sets treesource_error and frees the
fragment. The functions used by merge_fragment() become static.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Use the new constructors and return true on success
- Use dt_ref instead of fragment_ref
- Combine the label and path rules earlier, in the path-reference patch
dtc-parser.y | 53 ++++++++++++++++++++++++++--------------------------
dtc.h | 5 +----
livetree.c | 42 +++++++++++++++++++++++++++++++++++++----
3 files changed, 66 insertions(+), 34 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index abeee0d..9cda518 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -24,6 +24,22 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
+/*
+ * Merge a fragment into the tree as soon as it is parsed. The flags come
+ * from the actions using $<flags>-1, which relies on the rule being always:
+ * headers memreserves devicetree
+ * so $-1 is the value of headers
+ */
+static struct node *merge_parsed_fragment(struct node *dt, struct fragment *f,
+ unsigned int flags)
+{
+ if (!merge_fragment(&dt, f, flags & DTSF_PLUGIN))
+ treesource_error = true;
+ free_fragment(f);
+
+ return dt;
+}
+
%}
%union {
@@ -155,65 +171,50 @@ dt_ref: DT_LABEL_REF | DT_PATH_REF;
devicetree:
'/' nodedef
{
- $$ = name_node($2, "");
+ struct fragment *f = build_fragment_root($2);
+
+ $$ = merge_parsed_fragment(NULL, f, $<flags>-1);
}
| devicetree '/' nodedef
{
- $$ = merge_nodes($1, $3);
+ struct fragment *f = build_fragment_root($3);
+
+ $$ = merge_parsed_fragment($1, f, $<flags>-1);
}
| dt_ref nodedef
{
struct fragment *f;
f = build_fragment_ref(NULL, NULL, $1, &@1, $2);
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if (!merge_first_ref(&$$, f, $<flags>-1 & DTSF_PLUGIN))
- treesource_error = true;
+ $$ = merge_parsed_fragment(NULL, f, $<flags>-1);
}
| devicetree DT_LABEL dt_ref nodedef
{
struct fragment *f;
f = build_fragment_ref($2, &@2, $3, &@3, $4);
- if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
- treesource_error = true;
- $$ = $1;
+ $$ = merge_parsed_fragment($1, f, $<flags>-1);
}
| devicetree dt_ref nodedef
{
struct fragment *f;
f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if (!merge_ref($1, f, $<flags>-1 & DTSF_PLUGIN))
- treesource_error = true;
- $$ = $1;
+ $$ = merge_parsed_fragment($1, f, $<flags>-1);
}
| devicetree DT_DEL_NODE dt_ref ';'
{
struct fragment *f;
f = build_fragment_delete_node($3, &@3);
- if (!merge_delete_node($1, f))
- treesource_error = true;
- $$ = $1;
+ $$ = merge_parsed_fragment($1, f, $<flags>-1);
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
{
struct fragment *f;
f = build_fragment_omit_if_no_ref($3, &@3);
- if (!merge_omit_if_no_ref($1, f))
- treesource_error = true;
- $$ = $1;
+ $$ = merge_parsed_fragment($1, f, $<flags>-1);
}
;
diff --git a/dtc.h b/dtc.h
index 1befd5c..e223bc2 100644
--- a/dtc.h
+++ b/dtc.h
@@ -372,10 +372,7 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct srcpos *ref_srcpos);
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
-bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin);
-bool merge_ref(struct node *dt, struct fragment *f, bool plugin);
-bool merge_delete_node(struct node *dt, struct fragment *f);
-bool merge_omit_if_no_ref(struct node *dt, struct fragment *f);
+bool merge_fragment(struct node **dtp, struct fragment *f, bool plugin);
struct dt_info {
unsigned int dtsflags;
diff --git a/livetree.c b/livetree.c
index 26d39df..a1d5d71 100644
--- a/livetree.c
+++ b/livetree.c
@@ -580,7 +580,7 @@ static bool is_ref_relative(const char *ref)
*
* Returns true on success, false on an error, which has been reported.
*/
-bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin)
+static bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin)
{
bool ok = true;
@@ -611,7 +611,7 @@ bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin)
*
* Returns true on success, false on an error, which has been reported.
*/
-bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
+static bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
{
struct node *target;
bool ok = true;
@@ -651,7 +651,7 @@ bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
*
* Returns true on success, false on an error, which has been reported.
*/
-bool merge_delete_node(struct node *dt, struct fragment *f)
+static bool merge_delete_node(struct node *dt, struct fragment *f)
{
struct node *target = get_node_by_ref(dt, f->ref);
@@ -672,7 +672,7 @@ bool merge_delete_node(struct node *dt, struct fragment *f)
*
* Returns true on success, false on an error, which has been reported.
*/
-bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
+static bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
{
struct node *target = get_node_by_ref(dt, f->ref);
@@ -687,6 +687,40 @@ bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
return true;
}
+/*
+ * Merge a fragment of a dts file into the tree at *dtp, which is NULL for the
+ * first fragment. A reference is looked up in the tree as merged so far.
+ *
+ * Returns true on success, false on an error, which has been reported.
+ */
+bool merge_fragment(struct node **dtp, struct fragment *f, bool plugin)
+{
+ struct node *dt = *dtp;
+
+ if (!dt) {
+ /* The first fragment is the root or, in a plugin, a ref */
+ if (!f->ref) {
+ *dtp = name_node(f->node, "");
+ return true;
+ }
+ return merge_first_ref(dtp, f, plugin);
+ }
+
+ switch (f->type) {
+ case FRAGMENT_NODE:
+ if (f->ref)
+ return merge_ref(dt, f, plugin);
+ *dtp = merge_nodes(dt, f->node);
+ break;
+ case FRAGMENT_DELETE_NODE:
+ return merge_delete_node(dt, f);
+ case FRAGMENT_OMIT_IF_NO_REF:
+ return merge_omit_if_no_ref(dt, f);
+ }
+
+ return true;
+}
+
/*
* Tree accessor functions
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 13/15] dtc: Add helpers for the errors when merging a fragment
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (11 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 12/15] dtc: Add merge_fragment() to merge any fragment Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 14/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
2026-11-04 21:17 ` [PATCH v2 15/15] dtc: Merge the fragments of a dts file in main() Simon Glass
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
Three of the merge functions look up the node which a fragment refers to
and report 'Label or path not found' if it is missing, and two check
for a label-relative reference in a plugin. Each of these has its own
copy of the error, so a change to a message or its position must be made
in several places.
Add lookup_ref() to look up the node, reporting an error if it is not
found, and check_relative_ref() to report a label-relative reference.
Use these in the merge functions.
The output does not change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Return true from check_relative_ref() if the reference is OK
livetree.c | 75 +++++++++++++++++++++++++++++++++---------------------
1 file changed, 46 insertions(+), 29 deletions(-)
diff --git a/livetree.c b/livetree.c
index a1d5d71..a70a7ba 100644
--- a/livetree.c
+++ b/livetree.c
@@ -573,6 +573,40 @@ static bool is_ref_relative(const char *ref)
return ref[0] != '/' && strchr(&ref[1], '/');
}
+/*
+ * Report an error at pos if a fragment uses a label-relative reference, which
+ * a plugin does not support
+ *
+ * Returns true if the reference is OK, false if there is an error
+ */
+static bool check_relative_ref(struct fragment *f, struct srcpos *pos)
+{
+ if (!is_ref_relative(f->ref))
+ return true;
+ srcpos_error(pos, "Error",
+ "Label-relative reference %s not supported in plugin",
+ f->ref);
+
+ return false;
+}
+
+/*
+ * Look up the node which a fragment refers to, reporting an error if it is
+ * not found
+ *
+ * Returns the node, or NULL if it is not found
+ */
+static struct node *lookup_ref(struct node *dt, struct fragment *f)
+{
+ struct node *target = get_node_by_ref(dt, f->ref);
+
+ if (!target)
+ srcpos_error(f->ref_srcpos, "Error",
+ "Label or path %s not found", f->ref);
+
+ return target;
+}
+
/*
* Make the tree at *dtp from a first fragment which refers to a label or path,
* rather than being the root node. This is only valid in a plugin, where the
@@ -582,17 +616,14 @@ static bool is_ref_relative(const char *ref)
*/
static bool merge_first_ref(struct node **dtp, struct fragment *f, bool plugin)
{
- bool ok = true;
+ bool ok;
- if (!plugin) {
+ if (plugin) {
+ ok = check_relative_ref(f, f->node->srcpos);
+ } else {
srcpos_error(f->node->srcpos, "Error",
"Label or path %s not found", f->ref);
ok = false;
- } else if (is_ref_relative(f->ref)) {
- srcpos_error(f->node->srcpos, "Error",
- "Label-relative reference %s not supported in plugin",
- f->ref);
- ok = false;
}
/* Make a new, empty tree with a fragment@N node for the reference */
*dtp = add_orphan_node(name_node(build_node(NULL, NULL, NULL), ""),
@@ -618,13 +649,8 @@ static bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
assert(f->type == FRAGMENT_NODE);
if (plugin) {
- if (is_ref_relative(f->ref)) {
- srcpos_error(f->label ? f->label_srcpos : f->ref_srcpos,
- "Error",
- "Label-relative reference %s not supported in plugin",
- f->ref);
- ok = false;
- }
+ ok = check_relative_ref(f, f->label ? f->label_srcpos :
+ f->ref_srcpos);
if (!f->label &&
(f->ref[0] == '/' || !get_node_by_ref(dt, f->ref))) {
/* Add a fragment@N node for the reference */
@@ -633,12 +659,9 @@ static bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
}
}
- target = get_node_by_ref(dt, f->ref);
- if (!target) {
- srcpos_error(f->ref_srcpos, "Error",
- "Label or path %s not found", f->ref);
+ target = lookup_ref(dt, f);
+ if (!target)
return false;
- }
if (f->label)
add_label(&target->labels, f->label);
merge_nodes(target, f->node);
@@ -653,14 +676,11 @@ static bool merge_ref(struct node *dt, struct fragment *f, bool plugin)
*/
static bool merge_delete_node(struct node *dt, struct fragment *f)
{
- struct node *target = get_node_by_ref(dt, f->ref);
+ struct node *target = lookup_ref(dt, f);
assert(f->type == FRAGMENT_DELETE_NODE);
- if (!target) {
- srcpos_error(f->ref_srcpos, "Error",
- "Label or path %s not found", f->ref);
+ if (!target)
return false;
- }
delete_node(target);
return true;
@@ -674,14 +694,11 @@ static bool merge_delete_node(struct node *dt, struct fragment *f)
*/
static bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
{
- struct node *target = get_node_by_ref(dt, f->ref);
+ struct node *target = lookup_ref(dt, f);
assert(f->type == FRAGMENT_OMIT_IF_NO_REF);
- if (!target) {
- srcpos_error(f->ref_srcpos, "Error",
- "Label or path %s not found", f->ref);
+ if (!target)
return false;
- }
omit_node_if_unused(target);
return true;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 14/15] dtc: Merge the fragments of a dts file after parsing
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (12 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 13/15] dtc: Add helpers for the errors when merging a fragment Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
2026-11-04 21:17 ` [PATCH v2 15/15] dtc: Merge the fragments of a dts file in main() Simon Glass
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
We would like to deal with each top-level fragment of a dts file before
it is merged into the tree, but that requires the fragments to be kept
apart until the whole file is parsed. At present the parser merges each
one as soon as it is parsed. The grammar actions also need to know
whether the file is a plugin, which they find using $<flags>-1
Have the grammar build a list of the fragments instead, in source order,
passing it to build_dt_info() to store in struct dt_info, and merge them
with a new merge_fragments() once the whole file is parsed. This calls
merge_fragment() for each one, so the output does not change, and frees
each fragment once it is merged.
The $<flags>-1 term is no longer needed. Within a devicetree action it
reaches back up the parser stack to the value of headers, i.e. the flags
from /dts-v1/ and /plugin/. The sourcefile action passes the same value
to build_dt_info(), which stores it in dti->dtsflags, so
merge_fragments() can use that instead.
There are two differences in the errors. A syntax error later in the
file now stops dtc before any earlier reference errors are reported, and
reference errors now come after any other errors which the parser
reports, such as a division by zero, rather than in source order.
Apart from these, dtc gives the same output and messages as before for
all 4246 .dts and .dtso files in Linux v7.3-12284-g3799eeb906c9, and for
the dts files in tests/
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
Link: https://lore.kernel.org/devicetree-compiler/auK0ERvxGCJGSc3p@gractus.seuss/
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Pass the list to build_dt_info(), adding it to struct dt_info here
- Return true on success from merge_fragments()
dtc-parser.y | 65 ++++++++++++++++------------------------------------
dtc.h | 6 +++--
flattree.c | 2 +-
fstree.c | 2 +-
livetree.c | 40 +++++++++++++++++++++++++++++---
5 files changed, 63 insertions(+), 52 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 9cda518..0ff04c7 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -24,22 +24,6 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
-/*
- * Merge a fragment into the tree as soon as it is parsed. The flags come
- * from the actions using $<flags>-1, which relies on the rule being always:
- * headers memreserves devicetree
- * so $-1 is the value of headers
- */
-static struct node *merge_parsed_fragment(struct node *dt, struct fragment *f,
- unsigned int flags)
-{
- if (!merge_fragment(&dt, f, flags & DTSF_PLUGIN))
- treesource_error = true;
- free_fragment(f);
-
- return dt;
-}
-
%}
%union {
@@ -58,6 +42,7 @@ static struct node *merge_parsed_fragment(struct node *dt, struct fragment *f,
struct node *node;
struct node *nodelist;
struct reserve_info *re;
+ struct fragment *fragment;
uint64_t integer;
unsigned int flags;
}
@@ -92,7 +77,7 @@ static struct node *merge_parsed_fragment(struct node *dt, struct fragment *f,
%type <proplist> proplist
%type <labelref> dt_ref
-%type <node> devicetree
+%type <fragment> devicetree
%type <node> nodedef
%type <node> subnode
%type <nodelist> subnodes
@@ -117,8 +102,9 @@ static struct node *merge_parsed_fragment(struct node *dt, struct fragment *f,
sourcefile:
headers memreserves devicetree
{
- parser_output = build_dt_info($1, $2, $3,
- guess_boot_cpuid($3));
+ parser_output = build_dt_info($1, $2, NULL, $3, 0);
+ if (!merge_fragments(parser_output))
+ treesource_error = true;
}
;
@@ -171,50 +157,39 @@ dt_ref: DT_LABEL_REF | DT_PATH_REF;
devicetree:
'/' nodedef
{
- struct fragment *f = build_fragment_root($2);
-
- $$ = merge_parsed_fragment(NULL, f, $<flags>-1);
+ $$ = build_fragment_root($2);
}
| devicetree '/' nodedef
{
- struct fragment *f = build_fragment_root($3);
-
- $$ = merge_parsed_fragment($1, f, $<flags>-1);
+ $$ = append_fragment($1, build_fragment_root($3));
}
| dt_ref nodedef
{
- struct fragment *f;
-
- f = build_fragment_ref(NULL, NULL, $1, &@1, $2);
- $$ = merge_parsed_fragment(NULL, f, $<flags>-1);
+ $$ = build_fragment_ref(NULL, NULL, $1, &@1, $2);
}
| devicetree DT_LABEL dt_ref nodedef
{
- struct fragment *f;
-
- f = build_fragment_ref($2, &@2, $3, &@3, $4);
- $$ = merge_parsed_fragment($1, f, $<flags>-1);
+ $$ = append_fragment($1, build_fragment_ref($2, &@2,
+ $3, &@3,
+ $4));
}
| devicetree dt_ref nodedef
{
- struct fragment *f;
-
- f = build_fragment_ref(NULL, NULL, $2, &@2, $3);
- $$ = merge_parsed_fragment($1, f, $<flags>-1);
+ $$ = append_fragment($1, build_fragment_ref(NULL, NULL,
+ $2, &@2,
+ $3));
}
| devicetree DT_DEL_NODE dt_ref ';'
{
- struct fragment *f;
-
- f = build_fragment_delete_node($3, &@3);
- $$ = merge_parsed_fragment($1, f, $<flags>-1);
+ $$ = append_fragment($1,
+ build_fragment_delete_node($3,
+ &@3));
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
{
- struct fragment *f;
-
- f = build_fragment_omit_if_no_ref($3, &@3);
- $$ = merge_parsed_fragment($1, f, $<flags>-1);
+ $$ = append_fragment($1,
+ build_fragment_omit_if_no_ref($3,
+ &@3));
}
;
diff --git a/dtc.h b/dtc.h
index e223bc2..437efbc 100644
--- a/dtc.h
+++ b/dtc.h
@@ -372,13 +372,13 @@ struct fragment *build_fragment_omit_if_no_ref(char *ref,
struct srcpos *ref_srcpos);
struct fragment *append_fragment(struct fragment *list, struct fragment *new);
void free_fragment(struct fragment *f);
-bool merge_fragment(struct node **dtp, struct fragment *f, bool plugin);
struct dt_info {
unsigned int dtsflags;
struct reserve_info *reservelist;
uint32_t boot_cpuid_phys;
struct node *dt; /* the device tree */
+ struct fragment *fragments; /* not yet merged into dt */
const char *outname; /* filename being written to, "-" for stdout */
};
@@ -388,7 +388,9 @@ struct dt_info {
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
- struct node *tree, uint32_t boot_cpuid_phys);
+ struct node *tree, struct fragment *fragments,
+ uint32_t boot_cpuid_phys);
+bool merge_fragments(struct dt_info *dti);
void sort_tree(struct dt_info *dti);
void generate_labels_from_tree(struct dt_info *dti, const char *name);
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph);
diff --git a/flattree.c b/flattree.c
index f3b698c..aa183a0 100644
--- a/flattree.c
+++ b/flattree.c
@@ -923,5 +923,5 @@ struct dt_info *dt_from_blob(const char *fname)
if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
dtsflags |= DTSF_PLUGIN;
- return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys);
+ return build_dt_info(dtsflags, reservelist, tree, NULL, boot_cpuid_phys);
}
diff --git a/fstree.c b/fstree.c
index 0f9a534..88ff4ba 100644
--- a/fstree.c
+++ b/fstree.c
@@ -72,5 +72,5 @@ struct dt_info *dt_from_fs(const char *dirname)
tree = read_fstree(dirname);
tree = name_node(tree, "");
- return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
+ return build_dt_info(DTSF_V1, NULL, tree, NULL, guess_boot_cpuid(tree));
}
diff --git a/livetree.c b/livetree.c
index a70a7ba..f2c7703 100644
--- a/livetree.c
+++ b/livetree.c
@@ -552,7 +552,8 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
- struct node *tree, uint32_t boot_cpuid_phys)
+ struct node *tree, struct fragment *fragments,
+ uint32_t boot_cpuid_phys)
{
struct dt_info *dti;
@@ -560,9 +561,11 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
dti->dtsflags = dtsflags;
dti->reservelist = reservelist;
dti->dt = tree;
+ dti->fragments = fragments;
dti->boot_cpuid_phys = boot_cpuid_phys;
- fill_fullpaths(dti->dt, "");
+ if (tree)
+ fill_fullpaths(dti->dt, "");
return dti;
}
@@ -710,7 +713,8 @@ static bool merge_omit_if_no_ref(struct node *dt, struct fragment *f)
*
* Returns true on success, false on an error, which has been reported.
*/
-bool merge_fragment(struct node **dtp, struct fragment *f, bool plugin)
+static bool merge_fragment(struct node **dtp, struct fragment *f,
+ bool plugin)
{
struct node *dt = *dtp;
@@ -738,6 +742,36 @@ bool merge_fragment(struct node **dtp, struct fragment *f, bool plugin)
return true;
}
+/*
+ * Merge the fragments of a dts file into a single tree, in source order.
+ *
+ * Returns true on success, false on errors, which have been reported.
+ */
+bool merge_fragments(struct dt_info *dti)
+{
+ bool plugin = dti->dtsflags & DTSF_PLUGIN;
+ struct fragment *f, *next;
+ struct node *dt = NULL;
+ bool ok = true;
+
+ if (!dti->fragments)
+ return true;
+
+ for (f = dti->fragments; f; f = next) {
+ next = f->next;
+ if (!merge_fragment(&dt, f, plugin))
+ ok = false;
+ free_fragment(f);
+ }
+
+ dti->dt = dt;
+ dti->fragments = NULL;
+ dti->boot_cpuid_phys = guess_boot_cpuid(dt);
+ fill_fullpaths(dti->dt, "");
+
+ return ok;
+}
+
/*
* Tree accessor functions
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 15/15] dtc: Merge the fragments of a dts file in main()
2026-11-04 21:17 [PATCH v2 00/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
` (13 preceding siblings ...)
2026-11-04 21:17 ` [PATCH v2 14/15] dtc: Merge the fragments of a dts file after parsing Simon Glass
@ 2026-11-04 21:17 ` Simon Glass
14 siblings, 0 replies; 22+ messages in thread
From: Simon Glass @ 2026-11-04 21:17 UTC (permalink / raw)
To: Devicetree Compiler; +Cc: Herve Codina, David Gibson, Simon Glass
We would like to run checks on each fragment of a dts file before it is
merged, but that requires dt_from_source() to return the fragments
unmerged, so that main() can decide what to do with them. At present
they are merged at the end of the parse.
Call merge_fragments() from main() instead, once the input is read. It
does nothing for dtb or fs input, which has no fragments.
When a reference cannot be resolved, dtc now reports 'Unable to merge
input tree' rather than 'Syntax error parsing input tree', since the
file was parsed successfully. Check for this message with
test_label_ref.dts, since no test checks it at present.
If the parser reports an error, dt_from_source() gives up before main()
can merge the fragments. Merge them first in that case, so that any
errors in the fragments are still reported.
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
Link: https://lore.kernel.org/devicetree-compiler/auK0ERvxGCJGSc3p@gractus.seuss/
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2:
- Update for merge_fragments() returning true on success
dtc-parser.y | 2 --
dtc.c | 2 ++
tests/run_tests.sh | 3 ++-
treesource.c | 5 ++++-
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 0ff04c7..33a0078 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -103,8 +103,6 @@ sourcefile:
headers memreserves devicetree
{
parser_output = build_dt_info($1, $2, NULL, $3, 0);
- if (!merge_fragments(parser_output))
- treesource_error = true;
}
;
diff --git a/dtc.c b/dtc.c
index e506837..73e5d76 100644
--- a/dtc.c
+++ b/dtc.c
@@ -299,6 +299,8 @@ int main(int argc, char *argv[])
else
die("Unknown input format \"%s\"\n", inform);
+ if (!merge_fragments(dti))
+ die("Unable to merge input tree\n");
dti->outname = outname;
if (depfile) {
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 4661216..12fb054 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -741,7 +741,8 @@ dtc_tests () {
run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb
run_dtc_test -I dts -O dtb -o dtc_tree1_merge_path.test.dtb "$SRCDIR/test_tree1_merge_path.dts"
tree1_tests dtc_tree1_merge_path.test.dtb test_tree1.dtb
- run_wrap_error_test $DTC -I dts -O dtb -o /dev/null "$SRCDIR/test_label_ref.dts"
+ run_wrap_error_msg_test "Unable to merge input tree" \
+ $DTC -I dts -O dtb -o /dev/null "$SRCDIR/test_label_ref.dts"
run_dtc_test -I dts -O dtb -o dtc_relref_merge.test.dtb "$SRCDIR/relref_merge.dts"
run_test relref_merge dtc_relref_merge.test.dtb
diff --git a/treesource.c b/treesource.c
index bf648bf..3fb9d83 100644
--- a/treesource.c
+++ b/treesource.c
@@ -25,8 +25,11 @@ struct dt_info *dt_from_source(const char *fname)
if (yyparse() != 0)
die("Unable to parse input tree\n");
- if (treesource_error)
+ if (treesource_error) {
+ /* Report any errors in the fragments as well, before giving up */
+ merge_fragments(parser_output);
die("Syntax error parsing input tree\n");
+ }
return parser_output;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread