Commit a843be44a0
Changed files (6)
src/arch/wasm/abi.zig
@@ -13,70 +13,55 @@ const Zcu = @import("../../Zcu.zig");
/// Defines how to pass a type as part of a function signature,
/// both for parameters as well as return values.
-pub const Class = enum { direct, indirect, none };
-
-const none: [2]Class = .{ .none, .none };
-const memory: [2]Class = .{ .indirect, .none };
-const direct: [2]Class = .{ .direct, .none };
+pub const Class = union(enum) {
+ direct: Type,
+ indirect,
+};
/// Classifies a given Zig type to determine how they must be passed
/// or returned as value within a wasm function.
-/// When all elements result in `.none`, no value must be passed in or returned.
-pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class {
+pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
const ip = &zcu.intern_pool;
- const target = zcu.getTarget();
- if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none;
+ assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
switch (ty.zigTypeTag(zcu)) {
+ .int, .@"enum", .error_set => return .{ .direct = ty },
+ .float => return .{ .direct = ty },
+ .bool => return .{ .direct = ty },
+ .vector => return .{ .direct = ty },
+ .array => return .indirect,
+ .optional => {
+ assert(ty.isPtrLikeOptional(zcu));
+ return .{ .direct = ty };
+ },
+ .pointer => {
+ assert(!ty.isSlice(zcu));
+ return .{ .direct = ty };
+ },
.@"struct" => {
const struct_type = zcu.typeToStruct(ty).?;
if (struct_type.layout == .@"packed") {
- if (ty.bitSize(zcu) <= 64) return direct;
- return .{ .direct, .direct };
+ return .{ .direct = ty };
}
if (struct_type.field_types.len > 1) {
// The struct type is non-scalar.
- return memory;
+ return .indirect;
}
const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]);
const explicit_align = struct_type.fieldAlign(ip, 0);
if (explicit_align != .none) {
if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))
- return memory;
+ return .indirect;
}
return classifyType(field_ty, zcu);
},
- .int, .@"enum", .error_set => {
- const int_bits = ty.intInfo(zcu).bits;
- if (int_bits <= 64) return direct;
- if (int_bits <= 128) return .{ .direct, .direct };
- return memory;
- },
- .float => {
- const float_bits = ty.floatBits(target);
- if (float_bits <= 64) return direct;
- if (float_bits <= 128) return .{ .direct, .direct };
- return memory;
- },
- .bool => return direct,
- .vector => return direct,
- .array => return memory,
- .optional => {
- assert(ty.isPtrLikeOptional(zcu));
- return direct;
- },
- .pointer => {
- assert(!ty.isSlice(zcu));
- return direct;
- },
.@"union" => {
const union_obj = zcu.typeToUnion(ty).?;
if (union_obj.flagsUnordered(ip).layout == .@"packed") {
- if (ty.bitSize(zcu) <= 64) return direct;
- return .{ .direct, .direct };
+ return .{ .direct = ty };
}
const layout = ty.unionGetLayout(zcu);
assert(layout.tag_size == 0);
- if (union_obj.field_types.len > 1) return memory;
+ if (union_obj.field_types.len > 1) return .indirect;
const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
return classifyType(first_field_ty, zcu);
},
@@ -97,32 +82,6 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class {
}
}
-/// Returns the scalar type a given type can represent.
-/// Asserts given type can be represented as scalar, such as
-/// a struct with a single scalar field.
-pub fn scalarType(ty: Type, zcu: *Zcu) Type {
- const ip = &zcu.intern_pool;
- switch (ty.zigTypeTag(zcu)) {
- .@"struct" => {
- if (zcu.typeToPackedStruct(ty)) |packed_struct| {
- return scalarType(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), zcu);
- } else {
- assert(ty.structFieldCount(zcu) == 1);
- return scalarType(ty.fieldType(0, zcu), zcu);
- }
- },
- .@"union" => {
- const union_obj = zcu.typeToUnion(ty).?;
- if (union_obj.flagsUnordered(ip).layout != .@"packed") {
- const layout = Type.getUnionLayout(union_obj, zcu);
- if (layout.payload_size == 0 and layout.tag_size != 0) {
- return scalarType(ty.unionTagTypeSafety(zcu).?, zcu);
- }
- assert(union_obj.field_types.len == 1);
- }
- const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
- return scalarType(first_field_ty, zcu);
- },
- else => return ty,
- }
+pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool {
+ return scalar_ty.bitSize(zcu) > 64;
}
src/arch/wasm/CodeGen.zig
@@ -1408,11 +1408,22 @@ fn resolveCallingConventionValues(
},
.wasm_mvp => {
for (fn_info.param_types.get(ip)) |ty| {
- const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu);
- for (ty_classes) |class| {
- if (class == .none) continue;
- try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
- result.local_index += 1;
+ if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) {
+ continue;
+ }
+ switch (abi.classifyType(.fromInterned(ty), zcu)) {
+ .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {
+ try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
+ result.local_index += 1;
+ } else {
+ try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
+ try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });
+ result.local_index += 2;
+ },
+ .indirect => {
+ try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
+ result.local_index += 1;
+ },
}
}
},
@@ -1428,14 +1439,13 @@ pub fn firstParamSRet(
zcu: *const Zcu,
target: *const std.Target,
) bool {
+ if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false;
switch (cc) {
.@"inline" => unreachable,
.auto => return isByRef(return_type, zcu, target),
- .wasm_mvp => {
- const ty_classes = abi.classifyType(return_type, zcu);
- if (ty_classes[0] == .indirect) return true;
- if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true;
- return false;
+ .wasm_mvp => switch (abi.classifyType(return_type, zcu)) {
+ .direct => |scalar_ty| return abi.lowerAsDoubleI64(scalar_ty, zcu),
+ .indirect => return true,
},
else => return false,
}
@@ -1449,26 +1459,19 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV
}
const zcu = cg.pt.zcu;
- const ty_classes = abi.classifyType(ty, zcu);
- assert(ty_classes[0] != .none);
- switch (ty.zigTypeTag(zcu)) {
- .@"struct", .@"union" => {
- if (ty_classes[0] == .indirect) {
- return cg.lowerToStack(value);
- }
- assert(ty_classes[0] == .direct);
- const scalar_type = abi.scalarType(ty, zcu);
- switch (value) {
- .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0),
- .dead => unreachable,
- else => try cg.emitWValue(value),
- }
- },
- .int, .float => {
- if (ty_classes[1] == .none) {
+
+ switch (abi.classifyType(ty, zcu)) {
+ .direct => |scalar_type| if (!abi.lowerAsDoubleI64(scalar_type, zcu)) {
+ if (!isByRef(ty, zcu, cg.target)) {
return cg.lowerToStack(value);
+ } else {
+ switch (value) {
+ .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0),
+ .dead => unreachable,
+ else => try cg.emitWValue(value),
+ }
}
- assert(ty_classes[0] == .direct and ty_classes[1] == .direct);
+ } else {
assert(ty.abiSize(zcu) == 16);
// in this case we have an integer or float that must be lowered as 2 i64's.
try cg.emitWValue(value);
@@ -1476,7 +1479,7 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV
try cg.emitWValue(value);
try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
},
- else => return cg.lowerToStack(value),
+ .indirect => return cg.lowerToStack(value),
}
}
@@ -2142,23 +2145,16 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
if (cg.return_value != .none) {
try cg.store(cg.return_value, operand, ret_ty, 0);
} else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
- switch (ret_ty.zigTypeTag(zcu)) {
- // Aggregate types can be lowered as a singular value
- .@"struct", .@"union" => {
- const scalar_type = abi.scalarType(ret_ty, zcu);
- try cg.emitWValue(operand);
- const opcode = buildOpcode(.{
- .op = .load,
- .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)),
- .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned,
- .valtype1 = typeToValtype(scalar_type, zcu, cg.target),
- });
- try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
- .offset = operand.offset(),
- .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?),
- });
+ switch (abi.classifyType(ret_ty, zcu)) {
+ .direct => |scalar_type| {
+ assert(!abi.lowerAsDoubleI64(scalar_type, zcu));
+ if (!isByRef(ret_ty, zcu, cg.target)) {
+ try cg.emitWValue(operand);
+ } else {
+ _ = try cg.load(operand, scalar_type, 0);
+ }
},
- else => try cg.emitWValue(operand),
+ .indirect => unreachable,
}
} else {
if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) {
@@ -2284,14 +2280,24 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie
break :result_value .none;
} else if (first_param_sret) {
break :result_value sret;
- // TODO: Make this less fragile and optimize
- } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") {
- const result_local = try cg.allocLocal(ret_ty);
- try cg.addLocal(.local_set, result_local.local.value);
- const scalar_type = abi.scalarType(ret_ty, zcu);
- const result = try cg.allocStack(scalar_type);
- try cg.store(result, result_local, scalar_type, 0);
- break :result_value result;
+ } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp) {
+ switch (abi.classifyType(ret_ty, zcu)) {
+ .direct => |scalar_type| {
+ assert(!abi.lowerAsDoubleI64(scalar_type, zcu));
+ if (!isByRef(ret_ty, zcu, cg.target)) {
+ const result_local = try cg.allocLocal(ret_ty);
+ try cg.addLocal(.local_set, result_local.local.value);
+ break :result_value result_local;
+ } else {
+ const result_local = try cg.allocLocal(ret_ty);
+ try cg.addLocal(.local_set, result_local.local.value);
+ const result = try cg.allocStack(ret_ty);
+ try cg.store(result, result_local, scalar_type, 0);
+ break :result_value result;
+ }
+ },
+ .indirect => unreachable,
+ }
} else {
const result_local = try cg.allocLocal(ret_ty);
try cg.addLocal(.local_set, result_local.local.value);
@@ -2597,26 +2603,17 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc;
const arg_ty = cg.typeOfIndex(inst);
if (cc == .wasm_mvp) {
- const arg_classes = abi.classifyType(arg_ty, zcu);
- for (arg_classes) |class| {
- if (class != .none) {
+ switch (abi.classifyType(arg_ty, zcu)) {
+ .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {
cg.arg_index += 1;
- }
- }
-
- // When we have an argument that's passed using more than a single parameter,
- // we combine them into a single stack value
- if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
- if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) {
- return cg.fail(
- "TODO: Implement C-ABI argument for type '{}'",
- .{arg_ty.fmt(pt)},
- );
- }
- const result = try cg.allocStack(arg_ty);
- try cg.store(result, arg, Type.u64, 0);
- try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
- return cg.finishAir(inst, result, &.{});
+ } else {
+ cg.arg_index += 2;
+ const result = try cg.allocStack(arg_ty);
+ try cg.store(result, arg, Type.u64, 0);
+ try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
+ return cg.finishAir(inst, result, &.{});
+ },
+ .indirect => cg.arg_index += 1,
}
} else {
cg.arg_index += 1;
src/codegen/llvm.zig
@@ -11723,7 +11723,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe
.x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory,
.x86_sysv, .x86_win => isByRef(return_type, zcu),
.x86_stdcall => !isScalar(zcu, return_type),
- .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu)[0] == .indirect,
+ .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect,
.aarch64_aapcs,
.aarch64_aapcs_darwin,
.aarch64_aapcs_win,
@@ -11808,18 +11808,9 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
return o.builder.structType(.normal, types[0..types_len]);
},
},
- .wasm_mvp => {
- if (isScalar(zcu, return_type)) {
- return o.lowerType(return_type);
- }
- const classes = wasm_c_abi.classifyType(return_type, zcu);
- if (classes[0] == .indirect or classes[0] == .none) {
- return .void;
- }
-
- assert(classes[0] == .direct and classes[1] == .none);
- const scalar_type = wasm_c_abi.scalarType(return_type, zcu);
- return o.builder.intType(@intCast(scalar_type.abiSize(zcu) * 8));
+ .wasm_mvp => switch (wasm_c_abi.classifyType(return_type, zcu)) {
+ .direct => |scalar_ty| return o.lowerType(scalar_ty),
+ .indirect => return .void,
},
// TODO investigate other callconvs
else => return o.lowerType(return_type),
@@ -12073,17 +12064,28 @@ const ParamTypeIterator = struct {
},
}
},
- .wasm_mvp => {
- it.zig_index += 1;
- it.llvm_index += 1;
- if (isScalar(zcu, ty)) {
- return .byval;
- }
- const classes = wasm_c_abi.classifyType(ty, zcu);
- if (classes[0] == .indirect) {
+ .wasm_mvp => switch (wasm_c_abi.classifyType(ty, zcu)) {
+ .direct => |scalar_ty| {
+ if (isScalar(zcu, ty)) {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ return .byval;
+ } else {
+ var types_buffer: [8]Builder.Type = undefined;
+ types_buffer[0] = try it.object.lowerType(scalar_ty);
+ it.types_buffer = types_buffer;
+ it.types_len = 1;
+ it.llvm_index += 1;
+ it.zig_index += 1;
+ return .multiple_llvm_types;
+ }
+ },
+ .indirect => {
+ it.zig_index += 1;
+ it.llvm_index += 1;
+ it.byval_attr = true;
return .byref;
- }
- return .abi_sized_int;
+ },
},
// TODO investigate other callconvs
else => {
src/link/Wasm.zig
@@ -4627,10 +4627,13 @@ fn convertZcuFnType(
try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle
} else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) {
if (cc == .wasm_mvp) {
- const res_classes = abi.classifyType(return_type, zcu);
- assert(res_classes[0] == .direct and res_classes[1] == .none);
- const scalar_type = abi.scalarType(return_type, zcu);
- try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
+ switch (abi.classifyType(return_type, zcu)) {
+ .direct => |scalar_ty| {
+ assert(!abi.lowerAsDoubleI64(scalar_ty, zcu));
+ try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target));
+ },
+ .indirect => unreachable,
+ }
} else {
try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target));
}
@@ -4645,18 +4648,16 @@ fn convertZcuFnType(
switch (cc) {
.wasm_mvp => {
- const param_classes = abi.classifyType(param_type, zcu);
- if (param_classes[1] == .none) {
- if (param_classes[0] == .direct) {
- const scalar_type = abi.scalarType(param_type, zcu);
- try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
- } else {
- try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target));
- }
- } else {
- // i128/f128
- try params_buffer.append(gpa, .i64);
- try params_buffer.append(gpa, .i64);
+ switch (abi.classifyType(param_type, zcu)) {
+ .direct => |scalar_ty| {
+ if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {
+ try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target));
+ } else {
+ try params_buffer.append(gpa, .i64);
+ try params_buffer.append(gpa, .i64);
+ }
+ },
+ .indirect => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
}
},
else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
test/c_abi/cfuncs.c
@@ -227,6 +227,38 @@ void c_struct_u64_u64_8(size_t, size_t, size_t, size_t, size_t, size_t, size_t,
assert_or_panic(s.b == 40);
}
+struct Struct_f32 {
+ float a;
+};
+
+struct Struct_f32 zig_ret_struct_f32(void);
+
+void zig_struct_f32(struct Struct_f32);
+
+struct Struct_f32 c_ret_struct_f32(void) {
+ return (struct Struct_f32){ 2.5f };
+}
+
+void c_struct_f32(struct Struct_f32 s) {
+ assert_or_panic(s.a == 2.5f);
+}
+
+struct Struct_f64 {
+ double a;
+};
+
+struct Struct_f64 zig_ret_struct_f64(void);
+
+void zig_struct_f64(struct Struct_f64);
+
+struct Struct_f64 c_ret_struct_f64(void) {
+ return (struct Struct_f64){ 2.5 };
+}
+
+void c_struct_f64(struct Struct_f64 s) {
+ assert_or_panic(s.a == 2.5);
+}
+
struct Struct_f32f32_f32 {
struct {
float b, c;
@@ -296,6 +328,13 @@ void c_struct_u32_union_u32_u32u32(struct Struct_u32_Union_u32_u32u32 s) {
assert_or_panic(s.b.c.e == 3);
}
+struct Struct_i32_i32 {
+ int32_t a;
+ int32_t b;
+};
+
+void zig_struct_i32_i32(struct Struct_i32_i32);
+
struct BigStruct {
uint64_t a;
uint64_t b;
@@ -2674,6 +2713,18 @@ void run_c_tests(void) {
}
#if !defined(ZIG_RISCV64)
+ {
+ struct Struct_f32 s = zig_ret_struct_f32();
+ assert_or_panic(s.a == 2.5f);
+ zig_struct_f32((struct Struct_f32){ 2.5f });
+ }
+
+ {
+ struct Struct_f64 s = zig_ret_struct_f64();
+ assert_or_panic(s.a == 2.5);
+ zig_struct_f64((struct Struct_f64){ 2.5 });
+ }
+
{
struct Struct_f32f32_f32 s = zig_ret_struct_f32f32_f32();
assert_or_panic(s.a.b == 1.0f);
@@ -2699,6 +2750,10 @@ void run_c_tests(void) {
assert_or_panic(s.b.c.e == 3);
zig_struct_u32_union_u32_u32u32(s);
}
+ {
+ struct Struct_i32_i32 s = {1, 2};
+ zig_struct_i32_i32(s);
+ }
#endif
{
@@ -5024,6 +5079,21 @@ double complex c_cmultd(double complex a, double complex b) {
return 1.5 + I * 13.5;
}
+struct Struct_i32_i32 c_mut_struct_i32_i32(struct Struct_i32_i32 s) {
+ assert_or_panic(s.a == 1);
+ assert_or_panic(s.b == 2);
+ s.a += 100;
+ s.b += 250;
+ assert_or_panic(s.a == 101);
+ assert_or_panic(s.b == 252);
+ return s;
+}
+
+void c_struct_i32_i32(struct Struct_i32_i32 s) {
+ assert_or_panic(s.a == 1);
+ assert_or_panic(s.b == 2);
+}
+
void c_big_struct(struct BigStruct x) {
assert_or_panic(x.a == 1);
assert_or_panic(x.b == 2);
test/c_abi/main.zig
@@ -13,7 +13,7 @@ const expectEqual = std.testing.expectEqual;
const have_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isArm() and
!builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPowerPC32();
-const have_f128 = builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin();
+const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin());
const have_f80 = builtin.cpu.arch.isX86();
extern fn run_c_tests() void;
@@ -339,6 +339,56 @@ test "C ABI struct u64 u64" {
c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40 });
}
+const Struct_f32 = extern struct {
+ a: f32,
+};
+
+export fn zig_ret_struct_f32() Struct_f32 {
+ return .{ .a = 2.5 };
+}
+
+export fn zig_struct_f32(s: Struct_f32) void {
+ expect(s.a == 2.5) catch @panic("test failure");
+}
+
+extern fn c_ret_struct_f32() Struct_f32;
+
+extern fn c_struct_f32(Struct_f32) void;
+
+test "C ABI struct f32" {
+ if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
+ if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
+
+ const s = c_ret_struct_f32();
+ try expect(s.a == 2.5);
+ c_struct_f32(.{ .a = 2.5 });
+}
+
+const Struct_f64 = extern struct {
+ a: f64,
+};
+
+export fn zig_ret_struct_f64() Struct_f64 {
+ return .{ .a = 2.5 };
+}
+
+export fn zig_struct_f64(s: Struct_f64) void {
+ expect(s.a == 2.5) catch @panic("test failure");
+}
+
+extern fn c_ret_struct_f64() Struct_f64;
+
+extern fn c_struct_f64(Struct_f64) void;
+
+test "C ABI struct f64" {
+ if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
+ if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
+
+ const s = c_ret_struct_f64();
+ try expect(s.a == 2.5);
+ c_struct_f64(.{ .a = 2.5 });
+}
+
const Struct_f32f32_f32 = extern struct {
a: extern struct { b: f32, c: f32 },
d: f32,
@@ -434,6 +484,34 @@ test "C ABI struct{u32,union{u32,struct{u32,u32}}}" {
c_struct_u32_union_u32_u32u32(.{ .a = 1, .b = .{ .c = .{ .d = 2, .e = 3 } } });
}
+const Struct_i32_i32 = extern struct {
+ a: i32,
+ b: i32,
+};
+extern fn c_mut_struct_i32_i32(Struct_i32_i32) Struct_i32_i32;
+extern fn c_struct_i32_i32(Struct_i32_i32) void;
+
+test "C ABI struct i32 i32" {
+ if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest;
+ if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
+
+ const s: Struct_i32_i32 = .{
+ .a = 1,
+ .b = 2,
+ };
+ const mut_res = c_mut_struct_i32_i32(s);
+ try expect(s.a == 1);
+ try expect(s.b == 2);
+ try expect(mut_res.a == 101);
+ try expect(mut_res.b == 252);
+ c_struct_i32_i32(s);
+}
+
+export fn zig_struct_i32_i32(s: Struct_i32_i32) void {
+ expect(s.a == 1) catch @panic("test failure: zig_struct_i32_i32 1");
+ expect(s.b == 2) catch @panic("test failure: zig_struct_i32_i32 2");
+}
+
const BigStruct = extern struct {
a: u64,
b: u64,
@@ -5591,64 +5669,56 @@ test "f80 extra struct" {
try expect(a.b == 24);
}
-comptime {
- skip: {
- if (builtin.target.cpu.arch.isWasm()) break :skip;
-
- _ = struct {
- export fn zig_f128(x: f128) f128 {
- expect(x == 12) catch @panic("test failure");
- return 34;
- }
- extern fn c_f128(f128) f128;
- test "f128 bare" {
- if (!have_f128) return error.SkipZigTest;
+export fn zig_f128(x: f128) f128 {
+ expect(x == 12) catch @panic("test failure");
+ return 34;
+}
+extern fn c_f128(f128) f128;
+test "f128 bare" {
+ if (!have_f128) return error.SkipZigTest;
- const a = c_f128(12.34);
- try expect(@as(f64, @floatCast(a)) == 56.78);
- }
+ const a = c_f128(12.34);
+ try expect(@as(f64, @floatCast(a)) == 56.78);
+}
- const f128_struct = extern struct {
- a: f128,
- };
- export fn zig_f128_struct(a: f128_struct) f128_struct {
- expect(a.a == 12345) catch @panic("test failure");
- return .{ .a = 98765 };
- }
- extern fn c_f128_struct(f128_struct) f128_struct;
- test "f128 struct" {
- if (!have_f128) return error.SkipZigTest;
+const f128_struct = extern struct {
+ a: f128,
+};
+export fn zig_f128_struct(a: f128_struct) f128_struct {
+ expect(a.a == 12345) catch @panic("test failure");
+ return .{ .a = 98765 };
+}
+extern fn c_f128_struct(f128_struct) f128_struct;
+test "f128 struct" {
+ if (!have_f128) return error.SkipZigTest;
- const a = c_f128_struct(.{ .a = 12.34 });
- try expect(@as(f64, @floatCast(a.a)) == 56.78);
+ const a = c_f128_struct(.{ .a = 12.34 });
+ try expect(@as(f64, @floatCast(a.a)) == 56.78);
- const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 });
- try expect(@as(f64, @floatCast(b.a)) == 56.78);
- try expect(@as(f64, @floatCast(b.b)) == 43.21);
- }
+ const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 });
+ try expect(@as(f64, @floatCast(b.a)) == 56.78);
+ try expect(@as(f64, @floatCast(b.b)) == 43.21);
+}
- const f128_f128_struct = extern struct {
- a: f128,
- b: f128,
- };
- export fn zig_f128_f128_struct(a: f128_f128_struct) f128_f128_struct {
- expect(a.a == 13) catch @panic("test failure");
- expect(a.b == 57) catch @panic("test failure");
- return .{ .a = 24, .b = 68 };
- }
- extern fn c_f128_f128_struct(f128_f128_struct) f128_f128_struct;
- test "f128 f128 struct" {
- if (!have_f128) return error.SkipZigTest;
+const f128_f128_struct = extern struct {
+ a: f128,
+ b: f128,
+};
+export fn zig_f128_f128_struct(a: f128_f128_struct) f128_f128_struct {
+ expect(a.a == 13) catch @panic("test failure");
+ expect(a.b == 57) catch @panic("test failure");
+ return .{ .a = 24, .b = 68 };
+}
+extern fn c_f128_f128_struct(f128_f128_struct) f128_f128_struct;
+test "f128 f128 struct" {
+ if (!have_f128) return error.SkipZigTest;
- const a = c_f128_struct(.{ .a = 12.34 });
- try expect(@as(f64, @floatCast(a.a)) == 56.78);
+ const a = c_f128_struct(.{ .a = 12.34 });
+ try expect(@as(f64, @floatCast(a.a)) == 56.78);
- const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 });
- try expect(@as(f64, @floatCast(b.a)) == 56.78);
- try expect(@as(f64, @floatCast(b.b)) == 43.21);
- }
- };
- }
+ const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 });
+ try expect(@as(f64, @floatCast(b.a)) == 56.78);
+ try expect(@as(f64, @floatCast(b.b)) == 43.21);
}
// The stdcall attribute on C functions is ignored when compiled on non-x86