diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index b4c866855..4ecfaad06 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -499,6 +499,18 @@ namespace lbAbi386 { } } + // A complex lowers to a struct of two floats. The struct rule sends every struct through + // a hidden pointer. The psABI gives complex its own rule: one of eight bytes or fewer + // comes back in EAX:EDX, and only the wider ones go through memory. `complex64` is + // returned coerced to `i64` and `complex128` keeps the hidden pointer. + if (return_is_defined && !return_is_tuple && + return_source != nullptr && is_type_complex(return_source)) { + i64 sz = lb_sizeof(return_type); + if (sz > 0 && sz <= 8) { + ft->ret = lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, cast(unsigned)(sz*8)), nullptr, nullptr); + } + } + ft->calling_convention = calling_convention; return ft; } diff --git a/tests/abi/gen.odin b/tests/abi/gen.odin index 47eb05ee3..685134f89 100644 --- a/tests/abi/gen.odin +++ b/tests/abi/gen.odin @@ -310,10 +310,22 @@ build :: proc() { // 32 bits. A callee compiled to rely on that reads the untouched high bits. // The sub-32-bit widths are the ones that have it; i32 and f32 are the controls // that must not. - for tag in ([]string{"i8", "u8", "i16", "u16", "bool", "i32", "u32", "u64", "f32"}) { + for tag in ([]string{ + "i8", "u8", "i16", "u16", "bool", "i32", "u32", "u64", "f32", + // the kinds whose BARE form asks a different question from their wrapped one: a complex is + // two floats to the type system and a rule of its own to a psABI, and a bare `f16` is the + // narrowest float there is + "f16", "c64", "c128", "ptr", "enum", "bset", + }) { s := scalar(tag) v := val(0, tag) - expected := tag == "bool" ? odin_val(tag, v) : tp("%s(%s)", s.odin, v) + expected: string + switch tag { + case "ptr", "bool", "enum", "c64", "c128", "bset": + expected = odin_val(tag, v) + case: + expected = tp("%s(%s)", s.odin, v) + } add( tp("bs_%s", tag), s.odin,