-
-
Notifications
You must be signed in to change notification settings - Fork 43
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
Generating JSON with --opt=aggressive can result in out of bounds panics in certain cases #792
Comments
In https://github.com/inko-lang/inko/actions/runs/12588558439/job/35086680950 one test fails with |
A simpler way to reproduce this:
The resulting executable will sometimes panic, other times it straight up triggers a segmentation fault. |
Some extra notes:
The following program is an even easier way to reproduce it:
|
When the program segfaults, it appears to do so in the X86 |
The crash goes away if both |
A simpler way to reproduce the SEGV:
|
If I suspect the issue is how we handle struct arguments when generating the loads for method arguments. That is, if the local type is a struct and so is the argument, we just load the argument into that local as-is. These types might not be different though: diff --git a/compiler/src/llvm/passes.rs b/compiler/src/llvm/passes.rs
index 042bb59d..783e4ee1 100644
--- a/compiler/src/llvm/passes.rs
+++ b/compiler/src/llvm/passes.rs
@@ -1005,6 +1005,11 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
let var = self.variables[reg];
let typ = self.variable_types[reg];
+ if typ.is_struct_type() {
+ println!("local: {}", typ);
+ println!("arg: {}", arg);
+ }
+
// Depending on the ABI requirements we may pass a struct in as a
// pointer, but expect it as a value. In this case we need to load
// the argument pointer's value into the stack slot, instead of Produces:
|
Based on looking at what Rust does (example), it seems our implementation doesn't quite match Rust as much as I thought. That is, if the struct is between 8 and 16 bytes then Rust seems to express the struct as diff --git a/compiler/src/llvm/context.rs b/compiler/src/llvm/context.rs
index e3c78d2e..7d7fa302 100644
--- a/compiler/src/llvm/context.rs
+++ b/compiler/src/llvm/context.rs
@@ -236,7 +236,12 @@ impl Context {
// To avoid the complexity of that we take the same approach
// as Rust: if the struct is larger than 8 bytes, we turn it
// into two 64 bits values.
- ArgumentType::Regular(self.two_words().as_basic_type_enum())
+ let a = self.i64_type().into();
+ let b = self.custom_int(size_in_bits(bytes - 8)).into();
+
+ ArgumentType::Regular(
+ self.struct_type(&[a, b]).as_basic_type_enum(),
+ )
} else {
ArgumentType::StructValue(typ)
} Whether this is always correct is something I have yet to figure out. |
Some examples for what Rust does when you define a struct with
We should also verify what happens when the struct contains floats. Reading material: OpenSmalltalk/opensmalltalk-vm#443 (comment) Based on this, the algorithm seems to be as follows: iterate over the fields in order, sum their sizes until they no longer fit in 8 bytes. Round that size up to 8 bytes, then use the rest for the second field. This is why |
The type definitions generated for small struct arguments and returns on AMD64 wasn't correct, resulting in segmentation faults when running code with --opt=aggressive. This fixes this issue and ensures we run tests with all optimization levels on both AMD64 and ARM64. This fixes #792.
The type definitions generated for small struct arguments and returns on AMD64 and ARM64 wasn't correct, resulting in segmentation faults when running code with --opt=aggressive. This fixes this issue and ensures we run tests with all optimization levels on both AMD64 and ARM64. This fixes #792.
To add: on ARM64 it's possible to pass up to 4 |
https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170 says the following:
This suggests that we should use the same approach as for AMD64, i.e. |
The type definitions generated for small struct arguments and returns on AMD64 and ARM64 wasn't correct, resulting in segmentation faults when running code with --opt=aggressive. This fixes this issue and ensures we run tests with all optimization levels on both AMD64 and ARM64. This fixes #792.
When accepting or returning structs, the data must be copied using memcpy into a temporary slot, this slot must then be loaded into the final slot. Without this, LLVM appears to generate the wrong code on platforms such as ARM64. This fixes #792.
When accepting or returning structs, the data must be copied using memcpy into a temporary slot, this slot must then be loaded into the final slot. Without this, LLVM appears to generate the wrong code on platforms such as ARM64. This fixes #792.
TODO:
|
When accepting or returning structs, the data must be copied using memcpy into a temporary slot, this slot must then be loaded into the final slot. Without this, LLVM appears to generate the wrong code on platforms such as ARM64. In addition, for ARM64 we now generate the correct layouts for homogeneous float structs (e.g. `{ f64, f64, f64, f64 }`). This fixes #792.
When accepting or returning structs, the data must be copied using memcpy into a temporary slot, this slot must then be loaded into the final slot. Without this, LLVM appears to generate the wrong code on platforms such as ARM64. In addition, for ARM64 we now generate the correct layouts for homogeneous float structs (e.g. `{ f64, f64, f64, f64 }`). This fixes #792.
Please describe the bug
The following program works fine when using
--opt=balanced
, but produces a panic when using--opt=aggressive
:The panic is as follows:
Operating system
Fedora
Inko version
main
Reading material
The text was updated successfully, but these errors were encountered: