Skip to content

Commit 48b22af

Browse files
authored
feat: add proper debug info for constants using DW_TAG_const_type (#1485)
* feat: add proper debug info for global constants using DW_TAG_const_type
1 parent d9f8fa8 commit 48b22af

File tree

25 files changed

+1672
-1207
lines changed

25 files changed

+1672
-1207
lines changed

compiler/plc_driver/src/tests/multi_files.rs

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,274 @@ fn multiple_files_in_different_locations_with_debug_info() {
117117
//The functions are defined correctly
118118
filtered_assert_snapshot!(results.join("\n"));
119119
}
120+
121+
#[test]
122+
fn forward_declared_constant_is_also_marked_constant() {
123+
// GIVEN 2 sources, one with a forward declaration of a constant
124+
// and the other with the definition of that constant.
125+
let src1 = SourceCode::new(
126+
"
127+
FUNCTION main : INT
128+
VAR
129+
f: foo;
130+
END_VAR
131+
mainProg(f.something_to_initialize);
132+
END_FUNCTION
133+
134+
",
135+
"external_file1.st",
136+
);
137+
let src2 = SourceCode::new(
138+
"
139+
VAR_GLOBAL CONSTANT
140+
a: INT := 10;
141+
END_VAR
142+
143+
PROGRAM mainProg
144+
VAR_INPUT
145+
a: INT;
146+
END_VAR
147+
END_PROGRAM
148+
149+
FUNCTION_BLOCK foo
150+
VAR
151+
something_to_initialize: INT := 10 + a;
152+
END_VAR
153+
END_FUNCTION_BLOCK
154+
",
155+
"external_file2.st",
156+
);
157+
158+
// WHEN they are generated
159+
let results = compile_with_root(vec![src1, src2], vec![], "root", DebugLevel::Full(5)).unwrap();
160+
161+
// THEN the constant is marked as constant in the generated code
162+
filtered_assert_snapshot!(results.join("\n"), @r###"
163+
; ModuleID = 'external_file1.st'
164+
source_filename = "external_file1.st"
165+
target datalayout = "[filtered]"
166+
target triple = "[filtered]"
167+
168+
%foo = type { i16 }
169+
%mainProg = type { i16 }
170+
171+
@__foo__init = external unnamed_addr constant %foo, !dbg !0
172+
@mainProg_instance = external global %mainProg, !dbg !8
173+
174+
define i16 @main() !dbg !18 {
175+
entry:
176+
%main = alloca i16, align 2
177+
%f = alloca %foo, align 8
178+
call void @llvm.dbg.declare(metadata %foo* %f, metadata !23, metadata !DIExpression()), !dbg !24
179+
%0 = bitcast %foo* %f to i8*
180+
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false)
181+
call void @llvm.dbg.declare(metadata i16* %main, metadata !25, metadata !DIExpression()), !dbg !26
182+
store i16 0, i16* %main, align 2
183+
call void @__init_foo(%foo* %f), !dbg !27
184+
call void @__user_init_foo(%foo* %f), !dbg !27
185+
%something_to_initialize = getelementptr inbounds %foo, %foo* %f, i32 0, i32 0, !dbg !27
186+
%load_something_to_initialize = load i16, i16* %something_to_initialize, align 2, !dbg !27
187+
store i16 %load_something_to_initialize, i16* getelementptr inbounds (%mainProg, %mainProg* @mainProg_instance, i32 0, i32 0), align 2, !dbg !27
188+
call void @mainProg(%mainProg* @mainProg_instance), !dbg !28
189+
%main_ret = load i16, i16* %main, align 2, !dbg !29
190+
ret i16 %main_ret, !dbg !29
191+
}
192+
193+
declare !dbg !30 void @foo(%foo*)
194+
195+
declare void @__init_foo(%foo*)
196+
197+
declare !dbg !33 void @__user_init_foo(%foo*)
198+
199+
declare !dbg !38 void @mainProg(%mainProg*)
200+
201+
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
202+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
203+
204+
; Function Attrs: argmemonly nofree nounwind willreturn
205+
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1
206+
207+
attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
208+
attributes #1 = { argmemonly nofree nounwind willreturn }
209+
210+
!llvm.module.flags = !{!13, !14}
211+
!llvm.dbg.cu = !{!15}
212+
213+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
214+
!1 = distinct !DIGlobalVariable(name: "__foo__init", scope: !2, file: !2, line: 12, type: !3, isLocal: false, isDefinition: true)
215+
!2 = !DIFile(filename: "external_file2.st", directory: "")
216+
!3 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !4)
217+
!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", scope: !2, file: !2, line: 12, size: 16, align: 64, flags: DIFlagPublic, elements: !5, identifier: "foo")
218+
!5 = !{!6}
219+
!6 = !DIDerivedType(tag: DW_TAG_member, name: "something_to_initialize", scope: !2, file: !2, line: 14, baseType: !7, size: 16, align: 16, flags: DIFlagPublic)
220+
!7 = !DIBasicType(name: "INT", size: 16, encoding: DW_ATE_signed, flags: DIFlagPublic)
221+
!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression())
222+
!9 = distinct !DIGlobalVariable(name: "mainProg", scope: !2, file: !2, line: 6, type: !10, isLocal: false, isDefinition: true)
223+
!10 = !DICompositeType(tag: DW_TAG_structure_type, name: "mainProg", scope: !2, file: !2, line: 6, size: 16, align: 64, flags: DIFlagPublic, elements: !11, identifier: "mainProg")
224+
!11 = !{!12}
225+
!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !2, file: !2, line: 8, baseType: !7, size: 16, align: 16, flags: DIFlagPublic)
226+
!13 = !{i32 2, !"Dwarf Version", i32 5}
227+
!14 = !{i32 2, !"Debug Info Version", i32 3}
228+
!15 = distinct !DICompileUnit(language: DW_LANG_C, file: !16, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !17, splitDebugInlining: false)
229+
!16 = !DIFile(filename: "external_file1.st", directory: "root")
230+
!17 = !{!0, !8}
231+
!18 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !19, file: !19, line: 2, type: !20, scopeLine: 2, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !15, retainedNodes: !22)
232+
!19 = !DIFile(filename: "external_file1.st", directory: "")
233+
!20 = !DISubroutineType(flags: DIFlagPublic, types: !21)
234+
!21 = !{null}
235+
!22 = !{}
236+
!23 = !DILocalVariable(name: "f", scope: !18, file: !19, line: 4, type: !4, align: 64)
237+
!24 = !DILocation(line: 4, column: 8, scope: !18)
238+
!25 = !DILocalVariable(name: "main", scope: !18, file: !19, line: 2, type: !7, align: 16)
239+
!26 = !DILocation(line: 2, column: 13, scope: !18)
240+
!27 = !DILocation(line: 0, scope: !18)
241+
!28 = !DILocation(line: 6, column: 8, scope: !18)
242+
!29 = !DILocation(line: 7, column: 4, scope: !18)
243+
!30 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !2, file: !2, line: 12, type: !31, scopeLine: 16, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !15, retainedNodes: !22)
244+
!31 = !DISubroutineType(flags: DIFlagPublic, types: !32)
245+
!32 = !{null, !4}
246+
!33 = distinct !DISubprogram(name: "__user_init_foo", linkageName: "__user_init_foo", scope: !34, file: !34, type: !35, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !15, retainedNodes: !22)
247+
!34 = !DIFile(filename: "__initializers", directory: "")
248+
!35 = !DISubroutineType(flags: DIFlagPublic, types: !36)
249+
!36 = !{null, !37}
250+
!37 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "__auto_pointer_to_foo", baseType: !4, size: 64, align: 64, dwarfAddressSpace: 1)
251+
!38 = distinct !DISubprogram(name: "mainProg", linkageName: "mainProg", scope: !2, file: !2, line: 6, type: !39, scopeLine: 10, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !15, retainedNodes: !22)
252+
!39 = !DISubroutineType(flags: DIFlagPublic, types: !40)
253+
!40 = !{null, !10, !7}
254+
255+
; ModuleID = 'external_file2.st'
256+
source_filename = "external_file2.st"
257+
target datalayout = "[filtered]"
258+
target triple = "[filtered]"
259+
260+
%mainProg = type { i16 }
261+
%foo = type { i16 }
262+
263+
@a = unnamed_addr constant i16 10, !dbg !0
264+
@mainProg_instance = global %mainProg zeroinitializer, !dbg !5
265+
@__foo__init = unnamed_addr constant %foo { i16 20 }, !dbg !10
266+
267+
define void @mainProg(%mainProg* %0) !dbg !21 {
268+
entry:
269+
call void @llvm.dbg.declare(metadata %mainProg* %0, metadata !25, metadata !DIExpression()), !dbg !26
270+
%a = getelementptr inbounds %mainProg, %mainProg* %0, i32 0, i32 0
271+
ret void, !dbg !26
272+
}
273+
274+
define void @foo(%foo* %0) !dbg !27 {
275+
entry:
276+
call void @llvm.dbg.declare(metadata %foo* %0, metadata !30, metadata !DIExpression()), !dbg !31
277+
%this = alloca %foo*, align 8
278+
store %foo* %0, %foo** %this, align 8
279+
%something_to_initialize = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0
280+
ret void, !dbg !31
281+
}
282+
283+
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
284+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
285+
286+
attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
287+
288+
!llvm.module.flags = !{!16, !17}
289+
!llvm.dbg.cu = !{!18}
290+
291+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
292+
!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !2, line: 3, type: !3, isLocal: false, isDefinition: true)
293+
!2 = !DIFile(filename: "external_file2.st", directory: "")
294+
!3 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !4)
295+
!4 = !DIBasicType(name: "INT", size: 16, encoding: DW_ATE_signed, flags: DIFlagPublic)
296+
!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
297+
!6 = distinct !DIGlobalVariable(name: "mainProg", scope: !2, file: !2, line: 6, type: !7, isLocal: false, isDefinition: true)
298+
!7 = !DICompositeType(tag: DW_TAG_structure_type, name: "mainProg", scope: !2, file: !2, line: 6, size: 16, align: 64, flags: DIFlagPublic, elements: !8, identifier: "mainProg")
299+
!8 = !{!9}
300+
!9 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !2, file: !2, line: 8, baseType: !4, size: 16, align: 16, flags: DIFlagPublic)
301+
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
302+
!11 = distinct !DIGlobalVariable(name: "__foo__init", scope: !2, file: !2, line: 12, type: !12, isLocal: false, isDefinition: true)
303+
!12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13)
304+
!13 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", scope: !2, file: !2, line: 12, size: 16, align: 64, flags: DIFlagPublic, elements: !14, identifier: "foo")
305+
!14 = !{!15}
306+
!15 = !DIDerivedType(tag: DW_TAG_member, name: "something_to_initialize", scope: !2, file: !2, line: 14, baseType: !4, size: 16, align: 16, flags: DIFlagPublic)
307+
!16 = !{i32 2, !"Dwarf Version", i32 5}
308+
!17 = !{i32 2, !"Debug Info Version", i32 3}
309+
!18 = distinct !DICompileUnit(language: DW_LANG_C, file: !19, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !20, splitDebugInlining: false)
310+
!19 = !DIFile(filename: "external_file2.st", directory: "root")
311+
!20 = !{!0, !5, !10}
312+
!21 = distinct !DISubprogram(name: "mainProg", linkageName: "mainProg", scope: !2, file: !2, line: 6, type: !22, scopeLine: 10, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !18, retainedNodes: !24)
313+
!22 = !DISubroutineType(flags: DIFlagPublic, types: !23)
314+
!23 = !{null, !7, !4}
315+
!24 = !{}
316+
!25 = !DILocalVariable(name: "mainProg", scope: !21, file: !2, line: 10, type: !7)
317+
!26 = !DILocation(line: 10, column: 4, scope: !21)
318+
!27 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !2, file: !2, line: 12, type: !28, scopeLine: 16, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !18, retainedNodes: !24)
319+
!28 = !DISubroutineType(flags: DIFlagPublic, types: !29)
320+
!29 = !{null, !13}
321+
!30 = !DILocalVariable(name: "foo", scope: !27, file: !2, line: 16, type: !13)
322+
!31 = !DILocation(line: 16, column: 4, scope: !27)
323+
324+
; ModuleID = '__initializers'
325+
source_filename = "__initializers"
326+
target datalayout = "[filtered]"
327+
target triple = "[filtered]"
328+
329+
%mainProg = type { i16 }
330+
%foo = type { i16 }
331+
332+
@mainProg_instance = external global %mainProg
333+
@__foo__init = external unnamed_addr constant %foo
334+
335+
define void @__init_mainprog(%mainProg* %0) {
336+
entry:
337+
%self = alloca %mainProg*, align 8
338+
store %mainProg* %0, %mainProg** %self, align 8
339+
ret void
340+
}
341+
342+
declare void @mainProg(%mainProg*)
343+
344+
define void @__init_foo(%foo* %0) {
345+
entry:
346+
%self = alloca %foo*, align 8
347+
store %foo* %0, %foo** %self, align 8
348+
ret void
349+
}
350+
351+
declare void @foo(%foo*)
352+
353+
define void @__user_init_mainProg(%mainProg* %0) {
354+
entry:
355+
%self = alloca %mainProg*, align 8
356+
store %mainProg* %0, %mainProg** %self, align 8
357+
ret void
358+
}
359+
360+
define void @__user_init_foo(%foo* %0) {
361+
entry:
362+
%self = alloca %foo*, align 8
363+
store %foo* %0, %foo** %self, align 8
364+
ret void
365+
}
366+
367+
; ModuleID = '__init___TestProject'
368+
source_filename = "__init___TestProject"
369+
target datalayout = "[filtered]"
370+
target triple = "[filtered]"
371+
372+
%mainProg = type { i16 }
373+
374+
@mainProg_instance = external global %mainProg
375+
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___TestProject, i8* null }]
376+
377+
define void @__init___TestProject() {
378+
entry:
379+
call void @__init_mainprog(%mainProg* @mainProg_instance)
380+
call void @__user_init_mainProg(%mainProg* @mainProg_instance)
381+
ret void
382+
}
383+
384+
declare void @__init_mainprog(%mainProg*)
385+
386+
declare void @mainProg(%mainProg*)
387+
388+
declare void @__user_init_mainProg(%mainProg*)
389+
"###);
390+
}

src/codegen/debug.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,17 @@ impl<'ink> DebugBuilder<'ink> {
263263
self.types.insert(name.to_lowercase(), di_type);
264264
}
265265

266+
// Apply `DW_TAG_const_type` wrapper for constant variables
267+
fn apply_const_type_if_needed(&self, debug_type: DIType<'ink>, is_constant: bool) -> DIType<'ink> {
268+
if is_constant {
269+
let const_type =
270+
self.debug_info.create_reference_type(debug_type, 38 /* DW_TAG_const_type */);
271+
const_type.as_type()
272+
} else {
273+
debug_type
274+
}
275+
}
276+
266277
fn create_basic_type(
267278
&mut self,
268279
name: &str,
@@ -290,9 +301,9 @@ impl<'ink> DebugBuilder<'ink> {
290301
let index_types = members
291302
.iter()
292303
.filter(|it| !(it.is_temp() || it.is_variadic() || it.is_var_external()))
293-
.map(|it| (it.get_name(), it.get_type_name(), &it.source_location))
294-
.map(|(name, type_name, location)| {
295-
index.get_type(type_name.as_ref()).map(|dt| (name, dt, location))
304+
.map(|it| (it.get_name(), it.get_type_name(), &it.source_location, it.is_constant()))
305+
.map(|(name, type_name, location, is_constant)| {
306+
index.get_type(type_name.as_ref()).map(|dt| (name, dt, location, is_constant))
296307
})
297308
.collect::<Result<Vec<_>, Diagnostic>>()?;
298309
let struct_type = types_index.get_associated_type(name).map(|it| it.into_struct_type())?;
@@ -304,8 +315,9 @@ impl<'ink> DebugBuilder<'ink> {
304315

305316
let mut types = vec![];
306317

307-
for (element_index, (member_name, dt, location)) in index_types.iter().enumerate() {
318+
for (element_index, (member_name, dt, location, is_constant)) in index_types.iter().enumerate() {
308319
let di_type = self.get_or_create_debug_type(dt, index, types_index)?;
320+
let di_type = self.apply_const_type_if_needed(di_type.into(), *is_constant);
309321

310322
// Get the size and alignment from LLVM
311323
let llvm_type = types_index.find_associated_type(dt.get_name());
@@ -332,7 +344,7 @@ impl<'ink> DebugBuilder<'ink> {
332344
align_bits,
333345
offset_bits,
334346
DIFlags::PUBLIC,
335-
di_type.into(),
347+
di_type,
336348
)
337349
.as_type(),
338350
);
@@ -725,7 +737,9 @@ impl<'ink> Debug<'ink> for DebugBuilder<'ink> {
725737
location: &SourceLocation,
726738
) {
727739
if let Some(debug_type) = self.types.get(&type_name.to_lowercase()) {
728-
let debug_type = *debug_type;
740+
let debug_type =
741+
self.apply_const_type_if_needed((*debug_type).into(), global_variable.is_constant());
742+
729743
let file = location
730744
.get_file_name()
731745
.map(|it| self.get_or_create_debug_file(it))
@@ -736,7 +750,7 @@ impl<'ink> Debug<'ink> for DebugBuilder<'ink> {
736750
"",
737751
file,
738752
location.get_line_plus_one() as u32,
739-
debug_type.into(),
753+
debug_type,
740754
false,
741755
None,
742756
None,
@@ -769,12 +783,14 @@ impl<'ink> Debug<'ink> for DebugBuilder<'ink> {
769783
.map(|it| it.as_debug_info_scope())
770784
.unwrap_or_else(|| file.as_debug_info_scope());
771785
if let Some(debug_type) = self.types.get(&type_name.to_lowercase()) {
786+
let debug_type = self.apply_const_type_if_needed((*debug_type).into(), variable.is_constant());
787+
772788
let debug_variable = self.debug_info.create_auto_variable(
773789
scope,
774790
variable.get_name(),
775791
file,
776792
line,
777-
(*debug_type).into(),
793+
debug_type,
778794
false,
779795
DIFlagsConstants::ZERO,
780796
alignment,
@@ -808,13 +824,15 @@ impl<'ink> Debug<'ink> for DebugBuilder<'ink> {
808824
.unwrap_or_else(|| file.as_debug_info_scope());
809825

810826
if let Some(debug_type) = self.types.get(&type_name.to_lowercase()) {
827+
let debug_type = self.apply_const_type_if_needed((*debug_type).into(), variable.is_constant());
828+
811829
let debug_variable = self.debug_info.create_parameter_variable(
812830
scope,
813831
variable.get_name(),
814832
arg_no as u32,
815833
file,
816834
line,
817-
(*debug_type).into(),
835+
debug_type,
818836
false,
819837
DIFlagsConstants::ZERO,
820838
);

0 commit comments

Comments
 (0)