mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
wasm-c-abi: llvm fix struct handling + reorganize
I changed to `wasm/abi.zig`, this design is certainly better than the previous one. Still there is some conflict of interest between llvm and self-hosted backend, better design will appear when abi tests will be tested with self-hosted. Resolves: #23304 Resolves: #23305
This commit is contained in:
parent
0209c68fcc
commit
331bd83f11
6 changed files with 329 additions and 230 deletions
|
|
@ -1398,11 +1398,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;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1418,14 +1429,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,
|
||||
}
|
||||
|
|
@ -1439,26 +1449,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) {
|
||||
|
||||
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);
|
||||
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) {
|
||||
return cg.lowerToStack(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);
|
||||
|
|
@ -1466,7 +1469,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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2125,23 +2128,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)) {
|
||||
|
|
@ -2267,14 +2263,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);
|
||||
|
|
@ -2547,26 +2553,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12094,7 +12094,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,
|
||||
|
|
@ -12179,18 +12179,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),
|
||||
|
|
@ -12444,17 +12435,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 => {
|
||||
|
|
|
|||
|
|
@ -4617,10 +4617,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));
|
||||
}
|
||||
|
|
@ -4635,18 +4638,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)),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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;
|
||||
|
||||
_ = 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;
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue