Skip to content

Commit

Permalink
allow assignments between different pointer types (#466)
Browse files Browse the repository at this point in the history
casts between pointer types will generate a llvm bitcast statement

e.g.
x : INT; p : POINTER TO BYTE;
p := &x;

fixes #463

Co-authored-by: Ghaith Hachem <[email protected]>
  • Loading branch information
riederm and ghaith authored Feb 16, 2022
1 parent 4a7b8a6 commit 596aa03
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 6 deletions.
23 changes: 21 additions & 2 deletions src/codegen/llvm_typesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ fn create_llvm_extend_int_value<'a>(
}
}

///
/// generates a cast from the given `value` to the given `target_type` if necessary and returns the casted value. It returns
/// the original `value` if no cast is necessary
///
/// - `llvm` the llvm utilities to use for code-generation
/// - `index` the current Index used for type-lookups
/// - `llvm_type_index` the type index to lookup llvm generated types
/// - `target_type` the expected target type of the value
/// - `value` the value to (maybe) cast
/// - `value_type` the current type of the given value
/// - `statement` the original statement as a context (e.g. for error reporting)
///
pub fn cast_if_needed<'ctx>(
llvm: &Llvm<'ctx>,
index: &Index,
Expand Down Expand Up @@ -303,8 +315,15 @@ pub fn cast_if_needed<'ctx>(
)
.into()),
DataTypeInformation::Pointer { .. } | DataTypeInformation::Void { .. } => {
//this is ok, no cast required
Ok(value)
let target_ptr_type =
llvm_type_index.get_associated_type(target_type.get_name())?;
if value.get_type() != target_ptr_type {
// bit-cast necessary
Ok(builder.build_bitcast(value, target_ptr_type, ""))
} else {
//this is ok, no cast required
Ok(value)
}
}
_ => Err(Diagnostic::casting_error(
value_type.get_name(),
Expand Down
69 changes: 68 additions & 1 deletion src/codegen/tests/expression_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ fn cast_lword_to_pointer() {
FUNCTION baz : INT
VAR
ptr_x : POINTER TO INT;
y : LWORD;
y : LWORD;
END_VAR;
ptr_x := y;
Expand All @@ -302,6 +302,73 @@ fn cast_lword_to_pointer() {
insta::assert_snapshot!(result);
}

#[test]
fn cast_between_pointer_types() {
let result = codegen(
r#"
PROGRAM baz
VAR
ptr_x : POINTER TO BYTE;
y : WORD;
END_VAR;
ptr_x := &y;
END_PROGRAM
"#,
);

//should result in bitcast conversion when assigning to ptr_x
insta::assert_snapshot!(result);
}

#[test]
fn unnecessary_casts_between_pointer_types() {
let result = codegen(
r#"
TYPE MyByte : BYTE; END_TYPE
PROGRAM baz
VAR
ptr : POINTER TO BYTE;
b : BYTE;
si : SINT;
mb : MyByte;
END_VAR;
ptr := &b; //no cast necessary
ptr := &si; //no cast necessary
ptr := &mb; //no cast necessary
END_PROGRAM
"#,
);

//should not result in bitcast
insta::assert_snapshot!(result);
}

#[test]
fn access_string_via_byte_array() {
let result = codegen(
r#"
TYPE MyByte : BYTE; END_TYPE
PROGRAM baz
VAR
str: STRING[10];
ptr : POINTER TO BYTE;
bytes : POINTER TO ARRAY[0..9] OF BYTE;
END_VAR;
ptr := &str; //bit-cast expected
bytes := &str;
END_PROGRAM
"#,
);

//should result in bitcasts
insta::assert_snapshot!(result);
}

#[test]
fn pointer_arithmetics() {
// codegen should be successful for binary expression for pointer<->int / int<->pointer / pointer<->pointer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: src/codegen/tests/code_gen_tests.rs
assertion_line: 1718
assertion_line: 1719
expression: result

---
Expand All @@ -16,8 +16,8 @@ entry:
%X = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0
%pX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1
%rX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 2
store i32* null, i8** %pX, align 8
store i32* null, i8** %rX, align 8
store i8* null, i8** %pX, align 8
store i8* null, i8** %rX, align 8
store i8* %X, i8** %pX, align 8
store i8* %X, i8** %rX, align 8
%deref = load i8*, i8** %pX, align 8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 370
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { [11 x i8], i8*, [10 x i8]* }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%str = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%bytes = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
%1 = bitcast [11 x i8]* %str to i8*
store i8* %1, i8** %ptr, align 8
%2 = bitcast [11 x i8]* %str to [10 x i8]*
store [10 x i8]* %2, [10 x i8]** %bytes, align 8
ret void
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 321
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { i8*, i16 }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%ptr_x = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%y = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%1 = bitcast i16* %y to i8*
store i8* %1, i8** %ptr_x, align 8
ret void
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 346
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { i8*, i8, i8, i8 }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%b = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%si = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
%mb = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 3
store i8* %b, i8** %ptr, align 8
store i8* %si, i8** %ptr, align 8
store i8* %mb, i8** %ptr, align 8
ret void
}

28 changes: 28 additions & 0 deletions src/resolver/tests/resolve_expressions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ fn pointer_expressions_resolve_types() {

assert_eq!(format!("{:?}", expected_types), format!("{:?}", type_names));
}

#[test]
fn array_expressions_resolve_types() {
let (unit, index) = index(
Expand Down Expand Up @@ -2921,6 +2922,33 @@ fn adress_of_is_annotated_correctly() {
}
}

#[test]
fn pointer_assignment_with_incompatible_types_hints_correctly() {
let (unit, mut index) = index(
"PROGRAM PRG
VAR
x : INT;
pt : POINTER TO BYTE;
END_VAR
pt := &x;
END_PROGRAM",
);

let annotations = annotate(&unit, &mut index);
let assignment = &unit.implementations[0].statements[0];

if let AstStatement::Assignment { left, right, .. } = assignment {
assert_type_and_hint!(&annotations, &index, left, "__PRG_pt", None);
assert_type_and_hint!(
&annotations,
&index,
right,
"POINTER_TO_INT",
Some("__PRG_pt")
);
}
}

#[test]
fn call_on_function_block_array() {
//GIVEN
Expand Down

0 comments on commit 596aa03

Please sign in to comment.