diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 97daf249b1..5c37a1247b 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -487,6 +487,53 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { addLLVMFnAttr(llvm_fn, "noreturn"); } + if (!calling_convention_allows_zig_types(cc)) { + // A simplistic and desperate attempt at making the compiler respect the + // target ABI for return types. + // This is just enough to avoid miscompiling the test suite, it will be + // better in stage2. + ZigType *int_type = return_type->id == ZigTypeIdInt ? return_type : + return_type->id == ZigTypeIdEnum ? return_type->data.enumeration.tag_int_type : + nullptr; + + if (int_type != nullptr) { + const bool is_signed = int_type->data.integral.is_signed; + const uint32_t bit_width = int_type->data.integral.bit_count; + bool should_extend = false; + + // Rough equivalent of Clang's isPromotableIntegerType. + switch (bit_width) { + case 1: // bool + case 8: // {un,}signed char + case 16: // {un,}signed short + should_extend = true; + break; + default: + break; + } + + switch (g->zig_target->arch) { + case ZigLLVM_sparcv9: + case ZigLLVM_riscv64: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + // Always extend to the register width. + should_extend = bit_width < 64; + break; + default: + break; + } + + // {zero,sign}-extend the result. + if (should_extend) { + if (is_signed) + addLLVMAttr(llvm_fn, 0, "signext"); + else + addLLVMAttr(llvm_fn, 0, "zeroext"); + } + } + } + if (fn->body_node != nullptr) { maybe_export_dll(g, llvm_fn, linkage); diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index 0e8204779a..05ae78d290 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -24,6 +24,16 @@ void zig_f32(float); void zig_f64(double); void zig_five_floats(float, float, float, float, float); +bool zig_ret_bool(); +uint8_t zig_ret_u8(); +uint16_t zig_ret_u16(); +uint32_t zig_ret_u32(); +uint64_t zig_ret_u64(); +int8_t zig_ret_i8(); +int16_t zig_ret_i16(); +int32_t zig_ret_i32(); +int64_t zig_ret_i64(); + void zig_ptr(void *); void zig_bool(bool); @@ -119,6 +129,20 @@ void run_c_tests(void) { assert_or_panic(res.d == 23); assert_or_panic(res.e == 24); } + + { + assert_or_panic(zig_ret_bool() == 1); + + assert_or_panic(zig_ret_u8() == 0xff); + assert_or_panic(zig_ret_u16() == 0xffff); + assert_or_panic(zig_ret_u32() == 0xffffffff); + assert_or_panic(zig_ret_u64() == 0xffffffffffffffff); + + assert_or_panic(zig_ret_i8() == -1); + assert_or_panic(zig_ret_i16() == -1); + assert_or_panic(zig_ret_i32() == -1); + assert_or_panic(zig_ret_i64() == -1); + } } void c_u8(uint8_t x) { @@ -236,3 +260,31 @@ void c_big_struct_floats(Vector5 vec) { assert_or_panic(vec.w == 69); assert_or_panic(vec.q == 55); } + +bool c_ret_bool() { + return 1; +} +uint8_t c_ret_u8() { + return 0xff; +} +uint16_t c_ret_u16() { + return 0xffff; +} +uint32_t c_ret_u32() { + return 0xffffffff; +} +uint64_t c_ret_u64() { + return 0xffffffffffffffff; +} +int8_t c_ret_i8() { + return -1; +} +int16_t c_ret_i16() { + return -1; +} +int32_t c_ret_i32() { + return -1; +} +int64_t c_ret_i64() { + return -1; +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 18e9858da4..51fcc406ee 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -284,3 +284,55 @@ test "C ABI structs of floats as parameter" { }; c_big_struct_floats(v5); } + +export fn zig_ret_bool() bool { + return true; +} +export fn zig_ret_u8() u8 { + return 0xff; +} +export fn zig_ret_u16() u16 { + return 0xffff; +} +export fn zig_ret_u32() u32 { + return 0xffffffff; +} +export fn zig_ret_u64() u64 { + return 0xffffffffffffffff; +} +export fn zig_ret_i8() i8 { + return -1; +} +export fn zig_ret_i16() i16 { + return -1; +} +export fn zig_ret_i32() i32 { + return -1; +} +export fn zig_ret_i64() i64 { + return -1; +} + +extern fn c_ret_bool() bool; +extern fn c_ret_u8() u8; +extern fn c_ret_u16() u16; +extern fn c_ret_u32() u32; +extern fn c_ret_u64() u64; +extern fn c_ret_i8() i8; +extern fn c_ret_i16() i16; +extern fn c_ret_i32() i32; +extern fn c_ret_i64() i64; + +test "C ABI integer return types" { + expect(c_ret_bool() == true); + + expect(c_ret_u8() == 0xff); + expect(c_ret_u16() == 0xffff); + expect(c_ret_u32() == 0xffffffff); + expect(c_ret_u64() == 0xffffffffffffffff); + + expect(c_ret_i8() == -1); + expect(c_ret_i16() == -1); + expect(c_ret_i32() == -1); + expect(c_ret_i64() == -1); +}