Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support referencing local variables in pointer initialization #1346

Merged
merged 14 commits into from
Oct 29, 2024
2 changes: 1 addition & 1 deletion src/codegen/generators/expression_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
} else {
// TODO: using the global context we could get a slice here; currently not possible because the
// global context isn't passed into codegen
Err(Diagnostic::new(&format!("{element:?} not a direct access"))
Err(Diagnostic::new(format!("{element:?} not a direct access"))
.with_error_code("E055")
.with_location(element.get_location()))
}?;
Expand Down
14 changes: 14 additions & 0 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,20 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
variable.source_location.get_column(),
);
}

if self
.index
.find_effective_type_by_name(variable.get_type_name())
.map(|it| it.get_type_information())
.is_some_and(|it| it.is_reference_to() || it.is_alias())
{
// aliases and reference to variables have special handling for initialization. initialize with a nullpointer
self.llvm.builder.build_store(
left,
left.get_type().get_element_type().into_pointer_type().const_null(),
);
continue;
};
let right_stmt =
self.index.get_const_expressions().maybe_get_constant_statement(&variable.initial_value);
self.generate_variable_initializer(variable, left, right_stmt, &exp_gen)?;
Expand Down
272 changes: 272 additions & 0 deletions src/codegen/tests/initialization_test/complex_initializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,3 +1368,275 @@ fn var_external_blocks_are_ignored_in_init_functions() {
}
"###)
}

#[test]
fn ref_to_local_member() {
let res = codegen(
r"
FUNCTION_BLOCK foo
VAR
s : STRING;
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }

@__foo__init = unnamed_addr constant %foo zeroinitializer

define void @foo(%foo* %0) {
entry:
%s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0
%ptr = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1
%alias = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2
%reference_to = getelementptr inbounds %foo, %foo* %0, i32 0, i32 3
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"

%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }

@__foo__init = external global %foo

define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
%deref = load %foo*, %foo** %self, align 8
%ptr = getelementptr inbounds %foo, %foo* %deref, i32 0, i32 1
%deref1 = load %foo*, %foo** %self, align 8
%s = getelementptr inbounds %foo, %foo* %deref1, i32 0, i32 0
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
%deref2 = load %foo*, %foo** %self, align 8
%alias = getelementptr inbounds %foo, %foo* %deref2, i32 0, i32 2
%deref3 = load %foo*, %foo** %self, align 8
%s4 = getelementptr inbounds %foo, %foo* %deref3, i32 0, i32 0
store [81 x i8]* %s4, [81 x i8]** %alias, align 8
%deref5 = load %foo*, %foo** %self, align 8
%reference_to = getelementptr inbounds %foo, %foo* %deref5, i32 0, i32 3
%deref6 = load %foo*, %foo** %self, align 8
%s7 = getelementptr inbounds %foo, %foo* %deref6, i32 0, i32 0
store [81 x i8]* %s7, [81 x i8]** %reference_to, align 8
ret void
}

declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"

@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]

define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn ref_to_local_member_shadows_global() {
let res = codegen(
r"
VAR_GLOBAL
s : STRING;
END_VAR

FUNCTION_BLOCK foo
VAR
s : STRING;
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }

@s = global [81 x i8] zeroinitializer
@__foo__init = unnamed_addr constant %foo zeroinitializer

define void @foo(%foo* %0) {
entry:
%s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0
%ptr = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1
%alias = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2
%reference_to = getelementptr inbounds %foo, %foo* %0, i32 0, i32 3
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"

%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }

@__foo__init = external global %foo

define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
%deref = load %foo*, %foo** %self, align 8
%ptr = getelementptr inbounds %foo, %foo* %deref, i32 0, i32 1
%deref1 = load %foo*, %foo** %self, align 8
%s = getelementptr inbounds %foo, %foo* %deref1, i32 0, i32 0
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
%deref2 = load %foo*, %foo** %self, align 8
%alias = getelementptr inbounds %foo, %foo* %deref2, i32 0, i32 2
%deref3 = load %foo*, %foo** %self, align 8
%s4 = getelementptr inbounds %foo, %foo* %deref3, i32 0, i32 0
store [81 x i8]* %s4, [81 x i8]** %alias, align 8
%deref5 = load %foo*, %foo** %self, align 8
%reference_to = getelementptr inbounds %foo, %foo* %deref5, i32 0, i32 3
%deref6 = load %foo*, %foo** %self, align 8
%s7 = getelementptr inbounds %foo, %foo* %deref6, i32 0, i32 0
store [81 x i8]* %s7, [81 x i8]** %reference_to, align 8
ret void
}

declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"

@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]

define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn temporary_variable_ref_to_local_member() {
let res = codegen(
r"
FUNCTION_BLOCK foo
VAR
s : STRING;
END_VAR
VAR_TEMP
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

%foo = type { [81 x i8] }

@__foo__init = unnamed_addr constant %foo zeroinitializer

define void @foo(%foo* %0) {
entry:
%s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0
%ptr = alloca [81 x i8]*, align 8
%alias = alloca [81 x i8]*, align 8
%reference_to = alloca [81 x i8]*, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* null, [81 x i8]** %alias, align 8
store [81 x i8]* null, [81 x i8]** %reference_to, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* %s, [81 x i8]** %alias, align 8
store [81 x i8]* %s, [81 x i8]** %reference_to, align 8
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"

%foo = type { [81 x i8] }

@__foo__init = external global %foo

define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
ret void
}

declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"

@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]

define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn temporary_variable_ref_to_temporary_variable() {
let res = codegen(
r"
FUNCTION foo
VAR
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
END_VAR
VAR_TEMP
s : STRING;
reference_to : REFERENCE TO STRING REF= alias;
END_VAR
FUNCTION
",
);

insta::assert_snapshot!(res, @r##"
; ModuleID = '<internal>'
source_filename = "<internal>"

define void @foo() {
entry:
%ptr = alloca [81 x i8]*, align 8
%alias = alloca [81 x i8]*, align 8
%s = alloca [81 x i8], align 1
%reference_to = alloca [81 x i8]*, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* null, [81 x i8]** %alias, align 8
%0 = bitcast [81 x i8]* %s to i8*
call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false)
store [81 x i8]* null, [81 x i8]** %reference_to, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* %s, [81 x i8]** %alias, align 8
%deref = load [81 x i8]*, [81 x i8]** %alias, align 8
store [81 x i8]* %deref, [81 x i8]** %reference_to, align 8
ret void
}

; Function Attrs: argmemonly nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #0

attributes #0 = { argmemonly nofree nounwind willreturn writeonly }
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"

@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]

define void @__init___testproject() {
entry:
ret void
}
"##)
}
15 changes: 6 additions & 9 deletions src/codegen/tests/statement_codegen_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ fn reference_to_string_assignment() {
VAR
a : REF_TO STRING;
END_VAR

a^ := 'hello';
END_FUNCTION
"#,
Expand Down Expand Up @@ -351,16 +350,15 @@ fn local_alias() {
"#,
);

assert_snapshot!(content, @r###"
assert_snapshot!(content, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

define void @main() {
entry:
%foo = alloca i32*, align 8
%bar = alloca i32, align 4
%load_bar = load i32, i32* %bar, align 4
store i32 %load_bar, i32** %foo, align 4
store i32* null, i32** %foo, align 8
store i32 0, i32* %bar, align 4
store i32* %bar, i32** %foo, align 8
ret void
Expand All @@ -374,7 +372,7 @@ fn local_alias() {
entry:
ret void
}
"###);
"#);
}

#[test]
Expand All @@ -390,16 +388,15 @@ fn local_string_alias() {
"#,
);

assert_snapshot!(content, @r###"
assert_snapshot!(content, @r##"
; ModuleID = '<internal>'
source_filename = "<internal>"

define void @main() {
entry:
%foo = alloca [81 x i8]*, align 8
%bar = alloca [81 x i8], align 1
%load_bar = load [81 x i8], [81 x i8]* %bar, align 1
store [81 x i8] %load_bar, [81 x i8]** %foo, align 1
store [81 x i8]* null, [81 x i8]** %foo, align 8
%0 = bitcast [81 x i8]* %bar to i8*
call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false)
store [81 x i8]* %bar, [81 x i8]** %foo, align 8
Expand All @@ -419,7 +416,7 @@ fn local_string_alias() {
entry:
ret void
}
"###);
"##);
}

#[test]
Expand Down
Loading
Loading