| author | |
| committer | |
| log | a843be44a0cd88d152116a11ba75d059bbb01073 |
| tree | 8f424e9ac8029a632d9ed43e5df7e944465040ce |
| parent | ad9cb401124e4b2d59e49bed2736aad7813e4f1d |
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: #233056 files changed, 329 insertions(+), 230 deletions(-)
src/arch/wasm/CodeGen.zig+69-72| ... | @@ -1408,11 +1408,22 @@ fn resolveCallingConventionValues( | ... | @@ -1408,11 +1408,22 @@ fn resolveCallingConventionValues( |
| 1408 | }, | 1408 | }, |
| 1409 | .wasm_mvp => { | 1409 | .wasm_mvp => { |
| 1410 | for (fn_info.param_types.get(ip)) |ty| { | 1410 | for (fn_info.param_types.get(ip)) |ty| { |
| 1411 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); | 1411 | if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) { |
| 1412 | for (ty_classes) |class| { | 1412 | continue; |
| 1413 | if (class == .none) continue; | 1413 | } |
| 1414 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | 1414 | switch (abi.classifyType(.fromInterned(ty), zcu)) { |
| 1415 | result.local_index += 1; | 1415 | .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 1416 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1417 | result.local_index += 1; | ||
| 1418 | } else { | ||
| 1419 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1420 | try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } }); | ||
| 1421 | result.local_index += 2; | ||
| 1422 | }, | ||
| 1423 | .indirect => { | ||
| 1424 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1425 | result.local_index += 1; | ||
| 1426 | }, | ||
| 1416 | } | 1427 | } |
| 1417 | } | 1428 | } |
| 1418 | }, | 1429 | }, |
| ... | @@ -1428,14 +1439,13 @@ pub fn firstParamSRet( | ... | @@ -1428,14 +1439,13 @@ pub fn firstParamSRet( |
| 1428 | zcu: *const Zcu, | 1439 | zcu: *const Zcu, |
| 1429 | target: *const std.Target, | 1440 | target: *const std.Target, |
| 1430 | ) bool { | 1441 | ) bool { |
| 1442 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false; | ||
| 1431 | switch (cc) { | 1443 | switch (cc) { |
| 1432 | .@"inline" => unreachable, | 1444 | .@"inline" => unreachable, |
| 1433 | .auto => return isByRef(return_type, zcu, target), | 1445 | .auto => return isByRef(return_type, zcu, target), |
| 1434 | .wasm_mvp => { | 1446 | .wasm_mvp => switch (abi.classifyType(return_type, zcu)) { |
| 1435 | const ty_classes = abi.classifyType(return_type, zcu); | 1447 | .direct => |scalar_ty| return abi.lowerAsDoubleI64(scalar_ty, zcu), |
| 1436 | if (ty_classes[0] == .indirect) return true; | 1448 | .indirect => return true, |
| 1437 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; | ||
| 1438 | return false; | ||
| 1439 | }, | 1449 | }, |
| 1440 | else => return false, | 1450 | else => return false, |
| 1441 | } | 1451 | } |
| ... | @@ -1449,26 +1459,19 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV | ... | @@ -1449,26 +1459,19 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV |
| 1449 | } | 1459 | } |
| 1450 | 1460 | ||
| 1451 | const zcu = cg.pt.zcu; | 1461 | const zcu = cg.pt.zcu; |
| 1452 | const ty_classes = abi.classifyType(ty, zcu); | 1462 | |
| 1453 | assert(ty_classes[0] != .none); | 1463 | switch (abi.classifyType(ty, zcu)) { |
| 1454 | switch (ty.zigTypeTag(zcu)) { | 1464 | .direct => |scalar_type| if (!abi.lowerAsDoubleI64(scalar_type, zcu)) { |
| 1455 | .@"struct", .@"union" => { | 1465 | if (!isByRef(ty, zcu, cg.target)) { |
| 1456 | if (ty_classes[0] == .indirect) { | ||
| 1457 | return cg.lowerToStack(value); | ||
| 1458 | } | ||
| 1459 | assert(ty_classes[0] == .direct); | ||
| 1460 | const scalar_type = abi.scalarType(ty, zcu); | ||
| 1461 | switch (value) { | ||
| 1462 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), | ||
| 1463 | .dead => unreachable, | ||
| 1464 | else => try cg.emitWValue(value), | ||
| 1465 | } | ||
| 1466 | }, | ||
| 1467 | .int, .float => { | ||
| 1468 | if (ty_classes[1] == .none) { | ||
| 1469 | return cg.lowerToStack(value); | 1466 | return cg.lowerToStack(value); |
| 1467 | } else { | ||
| 1468 | switch (value) { | ||
| 1469 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), | ||
| 1470 | .dead => unreachable, | ||
| 1471 | else => try cg.emitWValue(value), | ||
| 1472 | } | ||
| 1470 | } | 1473 | } |
| 1471 | assert(ty_classes[0] == .direct and ty_classes[1] == .direct); | 1474 | } else { |
| 1472 | assert(ty.abiSize(zcu) == 16); | 1475 | assert(ty.abiSize(zcu) == 16); |
| 1473 | // in this case we have an integer or float that must be lowered as 2 i64's. | 1476 | // in this case we have an integer or float that must be lowered as 2 i64's. |
| 1474 | try cg.emitWValue(value); | 1477 | try cg.emitWValue(value); |
| ... | @@ -1476,7 +1479,7 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV | ... | @@ -1476,7 +1479,7 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV |
| 1476 | try cg.emitWValue(value); | 1479 | try cg.emitWValue(value); |
| 1477 | try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); | 1480 | try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); |
| 1478 | }, | 1481 | }, |
| 1479 | else => return cg.lowerToStack(value), | 1482 | .indirect => return cg.lowerToStack(value), |
| 1480 | } | 1483 | } |
| 1481 | } | 1484 | } |
| 1482 | 1485 | ||
| ... | @@ -2142,23 +2145,16 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2142,23 +2145,16 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2142 | if (cg.return_value != .none) { | 2145 | if (cg.return_value != .none) { |
| 2143 | try cg.store(cg.return_value, operand, ret_ty, 0); | 2146 | try cg.store(cg.return_value, operand, ret_ty, 0); |
| 2144 | } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2147 | } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2145 | switch (ret_ty.zigTypeTag(zcu)) { | 2148 | switch (abi.classifyType(ret_ty, zcu)) { |
| 2146 | // Aggregate types can be lowered as a singular value | 2149 | .direct => |scalar_type| { |
| 2147 | .@"struct", .@"union" => { | 2150 | assert(!abi.lowerAsDoubleI64(scalar_type, zcu)); |
| 2148 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2151 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2149 | try cg.emitWValue(operand); | 2152 | try cg.emitWValue(operand); |
| 2150 | const opcode = buildOpcode(.{ | 2153 | } else { |
| 2151 | .op = .load, | 2154 | _ = try cg.load(operand, scalar_type, 0); |
| 2152 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), | 2155 | } |
| 2153 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, | ||
| 2154 | .valtype1 = typeToValtype(scalar_type, zcu, cg.target), | ||
| 2155 | }); | ||
| 2156 | try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ | ||
| 2157 | .offset = operand.offset(), | ||
| 2158 | .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?), | ||
| 2159 | }); | ||
| 2160 | }, | 2156 | }, |
| 2161 | else => try cg.emitWValue(operand), | 2157 | .indirect => unreachable, |
| 2162 | } | 2158 | } |
| 2163 | } else { | 2159 | } else { |
| 2164 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { | 2160 | 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 | ... | @@ -2284,14 +2280,24 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie |
| 2284 | break :result_value .none; | 2280 | break :result_value .none; |
| 2285 | } else if (first_param_sret) { | 2281 | } else if (first_param_sret) { |
| 2286 | break :result_value sret; | 2282 | break :result_value sret; |
| 2287 | // TODO: Make this less fragile and optimize | 2283 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp) { |
| 2288 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") { | 2284 | switch (abi.classifyType(ret_ty, zcu)) { |
| 2289 | const result_local = try cg.allocLocal(ret_ty); | 2285 | .direct => |scalar_type| { |
| 2290 | try cg.addLocal(.local_set, result_local.local.value); | 2286 | assert(!abi.lowerAsDoubleI64(scalar_type, zcu)); |
| 2291 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2287 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2292 | const result = try cg.allocStack(scalar_type); | 2288 | const result_local = try cg.allocLocal(ret_ty); |
| 2293 | try cg.store(result, result_local, scalar_type, 0); | 2289 | try cg.addLocal(.local_set, result_local.local.value); |
| 2294 | break :result_value result; | 2290 | break :result_value result_local; |
| 2291 | } else { | ||
| 2292 | const result_local = try cg.allocLocal(ret_ty); | ||
| 2293 | try cg.addLocal(.local_set, result_local.local.value); | ||
| 2294 | const result = try cg.allocStack(ret_ty); | ||
| 2295 | try cg.store(result, result_local, scalar_type, 0); | ||
| 2296 | break :result_value result; | ||
| 2297 | } | ||
| 2298 | }, | ||
| 2299 | .indirect => unreachable, | ||
| 2300 | } | ||
| 2295 | } else { | 2301 | } else { |
| 2296 | const result_local = try cg.allocLocal(ret_ty); | 2302 | const result_local = try cg.allocLocal(ret_ty); |
| 2297 | try cg.addLocal(.local_set, result_local.local.value); | 2303 | try cg.addLocal(.local_set, result_local.local.value); |
| ... | @@ -2597,26 +2603,17 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2597,26 +2603,17 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2597 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; | 2603 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; |
| 2598 | const arg_ty = cg.typeOfIndex(inst); | 2604 | const arg_ty = cg.typeOfIndex(inst); |
| 2599 | if (cc == .wasm_mvp) { | 2605 | if (cc == .wasm_mvp) { |
| 2600 | const arg_classes = abi.classifyType(arg_ty, zcu); | 2606 | switch (abi.classifyType(arg_ty, zcu)) { |
| 2601 | for (arg_classes) |class| { | 2607 | .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 2602 | if (class != .none) { | ||
| 2603 | cg.arg_index += 1; | 2608 | cg.arg_index += 1; |
| 2604 | } | 2609 | } else { |
| 2605 | } | 2610 | cg.arg_index += 2; |
| 2606 | 2611 | const result = try cg.allocStack(arg_ty); | |
| 2607 | // When we have an argument that's passed using more than a single parameter, | 2612 | try cg.store(result, arg, Type.u64, 0); |
| 2608 | // we combine them into a single stack value | 2613 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); |
| 2609 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { | 2614 | return cg.finishAir(inst, result, &.{}); |
| 2610 | if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) { | 2615 | }, |
| 2611 | return cg.fail( | 2616 | .indirect => cg.arg_index += 1, |
| 2612 | "TODO: Implement C-ABI argument for type '{}'", | ||
| 2613 | .{arg_ty.fmt(pt)}, | ||
| 2614 | ); | ||
| 2615 | } | ||
| 2616 | const result = try cg.allocStack(arg_ty); | ||
| 2617 | try cg.store(result, arg, Type.u64, 0); | ||
| 2618 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); | ||
| 2619 | return cg.finishAir(inst, result, &.{}); | ||
| 2620 | } | 2617 | } |
| 2621 | } else { | 2618 | } else { |
| 2622 | cg.arg_index += 1; | 2619 | cg.arg_index += 1; |
src/arch/wasm/abi.zig+26-67| ... | @@ -13,70 +13,55 @@ const Zcu = @import("../../Zcu.zig"); | ... | @@ -13,70 +13,55 @@ const Zcu = @import("../../Zcu.zig"); |
| 13 | 13 | ||
| 14 | /// Defines how to pass a type as part of a function signature, | 14 | /// Defines how to pass a type as part of a function signature, |
| 15 | /// both for parameters as well as return values. | 15 | /// both for parameters as well as return values. |
| 16 | pub const Class = enum { direct, indirect, none }; | 16 | pub const Class = union(enum) { |
| 17 | 17 | direct: Type, | |
| 18 | const none: [2]Class = .{ .none, .none }; | 18 | indirect, |
| 19 | const memory: [2]Class = .{ .indirect, .none }; | 19 | }; |
| 20 | const direct: [2]Class = .{ .direct, .none }; | ||
| 21 | 20 | ||
| 22 | /// Classifies a given Zig type to determine how they must be passed | 21 | /// Classifies a given Zig type to determine how they must be passed |
| 23 | /// or returned as value within a wasm function. | 22 | /// or returned as value within a wasm function. |
| 24 | /// When all elements result in `.none`, no value must be passed in or returned. | 23 | pub fn classifyType(ty: Type, zcu: *const Zcu) Class { |
| 25 | pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class { | ||
| 26 | const ip = &zcu.intern_pool; | 24 | const ip = &zcu.intern_pool; |
| 27 | const target = zcu.getTarget(); | 25 | assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); |
| 28 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return none; | ||
| 29 | switch (ty.zigTypeTag(zcu)) { | 26 | switch (ty.zigTypeTag(zcu)) { |
| 27 | .int, .@"enum", .error_set => return .{ .direct = ty }, | ||
| 28 | .float => return .{ .direct = ty }, | ||
| 29 | .bool => return .{ .direct = ty }, | ||
| 30 | .vector => return .{ .direct = ty }, | ||
| 31 | .array => return .indirect, | ||
| 32 | .optional => { | ||
| 33 | assert(ty.isPtrLikeOptional(zcu)); | ||
| 34 | return .{ .direct = ty }; | ||
| 35 | }, | ||
| 36 | .pointer => { | ||
| 37 | assert(!ty.isSlice(zcu)); | ||
| 38 | return .{ .direct = ty }; | ||
| 39 | }, | ||
| 30 | .@"struct" => { | 40 | .@"struct" => { |
| 31 | const struct_type = zcu.typeToStruct(ty).?; | 41 | const struct_type = zcu.typeToStruct(ty).?; |
| 32 | if (struct_type.layout == .@"packed") { | 42 | if (struct_type.layout == .@"packed") { |
| 33 | if (ty.bitSize(zcu) <= 64) return direct; | 43 | return .{ .direct = ty }; |
| 34 | return .{ .direct, .direct }; | ||
| 35 | } | 44 | } |
| 36 | if (struct_type.field_types.len > 1) { | 45 | if (struct_type.field_types.len > 1) { |
| 37 | // The struct type is non-scalar. | 46 | // The struct type is non-scalar. |
| 38 | return memory; | 47 | return .indirect; |
| 39 | } | 48 | } |
| 40 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); | 49 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); |
| 41 | const explicit_align = struct_type.fieldAlign(ip, 0); | 50 | const explicit_align = struct_type.fieldAlign(ip, 0); |
| 42 | if (explicit_align != .none) { | 51 | if (explicit_align != .none) { |
| 43 | if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) | 52 | if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) |
| 44 | return memory; | 53 | return .indirect; |
| 45 | } | 54 | } |
| 46 | return classifyType(field_ty, zcu); | 55 | return classifyType(field_ty, zcu); |
| 47 | }, | 56 | }, |
| 48 | .int, .@"enum", .error_set => { | ||
| 49 | const int_bits = ty.intInfo(zcu).bits; | ||
| 50 | if (int_bits <= 64) return direct; | ||
| 51 | if (int_bits <= 128) return .{ .direct, .direct }; | ||
| 52 | return memory; | ||
| 53 | }, | ||
| 54 | .float => { | ||
| 55 | const float_bits = ty.floatBits(target); | ||
| 56 | if (float_bits <= 64) return direct; | ||
| 57 | if (float_bits <= 128) return .{ .direct, .direct }; | ||
| 58 | return memory; | ||
| 59 | }, | ||
| 60 | .bool => return direct, | ||
| 61 | .vector => return direct, | ||
| 62 | .array => return memory, | ||
| 63 | .optional => { | ||
| 64 | assert(ty.isPtrLikeOptional(zcu)); | ||
| 65 | return direct; | ||
| 66 | }, | ||
| 67 | .pointer => { | ||
| 68 | assert(!ty.isSlice(zcu)); | ||
| 69 | return direct; | ||
| 70 | }, | ||
| 71 | .@"union" => { | 57 | .@"union" => { |
| 72 | const union_obj = zcu.typeToUnion(ty).?; | 58 | const union_obj = zcu.typeToUnion(ty).?; |
| 73 | if (union_obj.flagsUnordered(ip).layout == .@"packed") { | 59 | if (union_obj.flagsUnordered(ip).layout == .@"packed") { |
| 74 | if (ty.bitSize(zcu) <= 64) return direct; | 60 | return .{ .direct = ty }; |
| 75 | return .{ .direct, .direct }; | ||
| 76 | } | 61 | } |
| 77 | const layout = ty.unionGetLayout(zcu); | 62 | const layout = ty.unionGetLayout(zcu); |
| 78 | assert(layout.tag_size == 0); | 63 | assert(layout.tag_size == 0); |
| 79 | if (union_obj.field_types.len > 1) return memory; | 64 | if (union_obj.field_types.len > 1) return .indirect; |
| 80 | const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); | 65 | const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); |
| 81 | return classifyType(first_field_ty, zcu); | 66 | return classifyType(first_field_ty, zcu); |
| 82 | }, | 67 | }, |
| ... | @@ -97,32 +82,6 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class { | ... | @@ -97,32 +82,6 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) [2]Class { |
| 97 | } | 82 | } |
| 98 | } | 83 | } |
| 99 | 84 | ||
| 100 | /// Returns the scalar type a given type can represent. | 85 | pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool { |
| 101 | /// Asserts given type can be represented as scalar, such as | 86 | return scalar_ty.bitSize(zcu) > 64; |
| 102 | /// a struct with a single scalar field. | ||
| 103 | pub fn scalarType(ty: Type, zcu: *Zcu) Type { | ||
| 104 | const ip = &zcu.intern_pool; | ||
| 105 | switch (ty.zigTypeTag(zcu)) { | ||
| 106 | .@"struct" => { | ||
| 107 | if (zcu.typeToPackedStruct(ty)) |packed_struct| { | ||
| 108 | return scalarType(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), zcu); | ||
| 109 | } else { | ||
| 110 | assert(ty.structFieldCount(zcu) == 1); | ||
| 111 | return scalarType(ty.fieldType(0, zcu), zcu); | ||
| 112 | } | ||
| 113 | }, | ||
| 114 | .@"union" => { | ||
| 115 | const union_obj = zcu.typeToUnion(ty).?; | ||
| 116 | if (union_obj.flagsUnordered(ip).layout != .@"packed") { | ||
| 117 | const layout = Type.getUnionLayout(union_obj, zcu); | ||
| 118 | if (layout.payload_size == 0 and layout.tag_size != 0) { | ||
| 119 | return scalarType(ty.unionTagTypeSafety(zcu).?, zcu); | ||
| 120 | } | ||
| 121 | assert(union_obj.field_types.len == 1); | ||
| 122 | } | ||
| 123 | const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); | ||
| 124 | return scalarType(first_field_ty, zcu); | ||
| 125 | }, | ||
| 126 | else => return ty, | ||
| 127 | } | ||
| 128 | } | 87 | } |
src/codegen/llvm.zig+25-23| ... | @@ -11723,7 +11723,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe | ... | @@ -11723,7 +11723,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe |
| 11723 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory, | 11723 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory, |
| 11724 | .x86_sysv, .x86_win => isByRef(return_type, zcu), | 11724 | .x86_sysv, .x86_win => isByRef(return_type, zcu), |
| 11725 | .x86_stdcall => !isScalar(zcu, return_type), | 11725 | .x86_stdcall => !isScalar(zcu, return_type), |
| 11726 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu)[0] == .indirect, | 11726 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect, |
| 11727 | .aarch64_aapcs, | 11727 | .aarch64_aapcs, |
| 11728 | .aarch64_aapcs_darwin, | 11728 | .aarch64_aapcs_darwin, |
| 11729 | .aarch64_aapcs_win, | 11729 | .aarch64_aapcs_win, |
| ... | @@ -11808,18 +11808,9 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu | ... | @@ -11808,18 +11808,9 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu |
| 11808 | return o.builder.structType(.normal, types[0..types_len]); | 11808 | return o.builder.structType(.normal, types[0..types_len]); |
| 11809 | }, | 11809 | }, |
| 11810 | }, | 11810 | }, |
| 11811 | .wasm_mvp => { | 11811 | .wasm_mvp => switch (wasm_c_abi.classifyType(return_type, zcu)) { |
| 11812 | if (isScalar(zcu, return_type)) { | 11812 | .direct => |scalar_ty| return o.lowerType(scalar_ty), |
| 11813 | return o.lowerType(return_type); | 11813 | .indirect => return .void, |
| 11814 | } | ||
| 11815 | const classes = wasm_c_abi.classifyType(return_type, zcu); | ||
| 11816 | if (classes[0] == .indirect or classes[0] == .none) { | ||
| 11817 | return .void; | ||
| 11818 | } | ||
| 11819 | |||
| 11820 | assert(classes[0] == .direct and classes[1] == .none); | ||
| 11821 | const scalar_type = wasm_c_abi.scalarType(return_type, zcu); | ||
| 11822 | return o.builder.intType(@intCast(scalar_type.abiSize(zcu) * 8)); | ||
| 11823 | }, | 11814 | }, |
| 11824 | // TODO investigate other callconvs | 11815 | // TODO investigate other callconvs |
| 11825 | else => return o.lowerType(return_type), | 11816 | else => return o.lowerType(return_type), |
| ... | @@ -12073,17 +12064,28 @@ const ParamTypeIterator = struct { | ... | @@ -12073,17 +12064,28 @@ const ParamTypeIterator = struct { |
| 12073 | }, | 12064 | }, |
| 12074 | } | 12065 | } |
| 12075 | }, | 12066 | }, |
| 12076 | .wasm_mvp => { | 12067 | .wasm_mvp => switch (wasm_c_abi.classifyType(ty, zcu)) { |
| 12077 | it.zig_index += 1; | 12068 | .direct => |scalar_ty| { |
| 12078 | it.llvm_index += 1; | 12069 | if (isScalar(zcu, ty)) { |
| 12079 | if (isScalar(zcu, ty)) { | 12070 | it.zig_index += 1; |
| 12080 | return .byval; | 12071 | it.llvm_index += 1; |
| 12081 | } | 12072 | return .byval; |
| 12082 | const classes = wasm_c_abi.classifyType(ty, zcu); | 12073 | } else { |
| 12083 | if (classes[0] == .indirect) { | 12074 | var types_buffer: [8]Builder.Type = undefined; |
| 12075 | types_buffer[0] = try it.object.lowerType(scalar_ty); | ||
| 12076 | it.types_buffer = types_buffer; | ||
| 12077 | it.types_len = 1; | ||
| 12078 | it.llvm_index += 1; | ||
| 12079 | it.zig_index += 1; | ||
| 12080 | return .multiple_llvm_types; | ||
| 12081 | } | ||
| 12082 | }, | ||
| 12083 | .indirect => { | ||
| 12084 | it.zig_index += 1; | ||
| 12085 | it.llvm_index += 1; | ||
| 12086 | it.byval_attr = true; | ||
| 12084 | return .byref; | 12087 | return .byref; |
| 12085 | } | 12088 | }, |
| 12086 | return .abi_sized_int; | ||
| 12087 | }, | 12089 | }, |
| 12088 | // TODO investigate other callconvs | 12090 | // TODO investigate other callconvs |
| 12089 | else => { | 12091 | else => { |
src/link/Wasm.zig+17-16| ... | @@ -4627,10 +4627,13 @@ fn convertZcuFnType( | ... | @@ -4627,10 +4627,13 @@ fn convertZcuFnType( |
| 4627 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle | 4627 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle |
| 4628 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | 4628 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4629 | if (cc == .wasm_mvp) { | 4629 | if (cc == .wasm_mvp) { |
| 4630 | const res_classes = abi.classifyType(return_type, zcu); | 4630 | switch (abi.classifyType(return_type, zcu)) { |
| 4631 | assert(res_classes[0] == .direct and res_classes[1] == .none); | 4631 | .direct => |scalar_ty| { |
| 4632 | const scalar_type = abi.scalarType(return_type, zcu); | 4632 | assert(!abi.lowerAsDoubleI64(scalar_ty, zcu)); |
| 4633 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | 4633 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target)); |
| 4634 | }, | ||
| 4635 | .indirect => unreachable, | ||
| 4636 | } | ||
| 4634 | } else { | 4637 | } else { |
| 4635 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); | 4638 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); |
| 4636 | } | 4639 | } |
| ... | @@ -4645,18 +4648,16 @@ fn convertZcuFnType( | ... | @@ -4645,18 +4648,16 @@ fn convertZcuFnType( |
| 4645 | 4648 | ||
| 4646 | switch (cc) { | 4649 | switch (cc) { |
| 4647 | .wasm_mvp => { | 4650 | .wasm_mvp => { |
| 4648 | const param_classes = abi.classifyType(param_type, zcu); | 4651 | switch (abi.classifyType(param_type, zcu)) { |
| 4649 | if (param_classes[1] == .none) { | 4652 | .direct => |scalar_ty| { |
| 4650 | if (param_classes[0] == .direct) { | 4653 | if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 4651 | const scalar_type = abi.scalarType(param_type, zcu); | 4654 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target)); |
| 4652 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | 4655 | } else { |
| 4653 | } else { | 4656 | try params_buffer.append(gpa, .i64); |
| 4654 | try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)); | 4657 | try params_buffer.append(gpa, .i64); |
| 4655 | } | 4658 | } |
| 4656 | } else { | 4659 | }, |
| 4657 | // i128/f128 | 4660 | .indirect => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), |
| 4658 | try params_buffer.append(gpa, .i64); | ||
| 4659 | try params_buffer.append(gpa, .i64); | ||
| 4660 | } | 4661 | } |
| 4661 | }, | 4662 | }, |
| 4662 | else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), | 4663 | else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), |
test/c_abi/cfuncs.c+70| ... | @@ -227,6 +227,38 @@ void c_struct_u64_u64_8(size_t, size_t, size_t, size_t, size_t, size_t, size_t, | ... | @@ -227,6 +227,38 @@ void c_struct_u64_u64_8(size_t, size_t, size_t, size_t, size_t, size_t, size_t, |
| 227 | assert_or_panic(s.b == 40); | 227 | assert_or_panic(s.b == 40); |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | struct Struct_f32 { | ||
| 231 | float a; | ||
| 232 | }; | ||
| 233 | |||
| 234 | struct Struct_f32 zig_ret_struct_f32(void); | ||
| 235 | |||
| 236 | void zig_struct_f32(struct Struct_f32); | ||
| 237 | |||
| 238 | struct Struct_f32 c_ret_struct_f32(void) { | ||
| 239 | return (struct Struct_f32){ 2.5f }; | ||
| 240 | } | ||
| 241 | |||
| 242 | void c_struct_f32(struct Struct_f32 s) { | ||
| 243 | assert_or_panic(s.a == 2.5f); | ||
| 244 | } | ||
| 245 | |||
| 246 | struct Struct_f64 { | ||
| 247 | double a; | ||
| 248 | }; | ||
| 249 | |||
| 250 | struct Struct_f64 zig_ret_struct_f64(void); | ||
| 251 | |||
| 252 | void zig_struct_f64(struct Struct_f64); | ||
| 253 | |||
| 254 | struct Struct_f64 c_ret_struct_f64(void) { | ||
| 255 | return (struct Struct_f64){ 2.5 }; | ||
| 256 | } | ||
| 257 | |||
| 258 | void c_struct_f64(struct Struct_f64 s) { | ||
| 259 | assert_or_panic(s.a == 2.5); | ||
| 260 | } | ||
| 261 | |||
| 230 | struct Struct_f32f32_f32 { | 262 | struct Struct_f32f32_f32 { |
| 231 | struct { | 263 | struct { |
| 232 | float b, c; | 264 | float b, c; |
| ... | @@ -296,6 +328,13 @@ void c_struct_u32_union_u32_u32u32(struct Struct_u32_Union_u32_u32u32 s) { | ... | @@ -296,6 +328,13 @@ void c_struct_u32_union_u32_u32u32(struct Struct_u32_Union_u32_u32u32 s) { |
| 296 | assert_or_panic(s.b.c.e == 3); | 328 | assert_or_panic(s.b.c.e == 3); |
| 297 | } | 329 | } |
| 298 | 330 | ||
| 331 | struct Struct_i32_i32 { | ||
| 332 | int32_t a; | ||
| 333 | int32_t b; | ||
| 334 | }; | ||
| 335 | |||
| 336 | void zig_struct_i32_i32(struct Struct_i32_i32); | ||
| 337 | |||
| 299 | struct BigStruct { | 338 | struct BigStruct { |
| 300 | uint64_t a; | 339 | uint64_t a; |
| 301 | uint64_t b; | 340 | uint64_t b; |
| ... | @@ -2674,6 +2713,18 @@ void run_c_tests(void) { | ... | @@ -2674,6 +2713,18 @@ void run_c_tests(void) { |
| 2674 | } | 2713 | } |
| 2675 | 2714 | ||
| 2676 | #if !defined(ZIG_RISCV64) | 2715 | #if !defined(ZIG_RISCV64) |
| 2716 | { | ||
| 2717 | struct Struct_f32 s = zig_ret_struct_f32(); | ||
| 2718 | assert_or_panic(s.a == 2.5f); | ||
| 2719 | zig_struct_f32((struct Struct_f32){ 2.5f }); | ||
| 2720 | } | ||
| 2721 | |||
| 2722 | { | ||
| 2723 | struct Struct_f64 s = zig_ret_struct_f64(); | ||
| 2724 | assert_or_panic(s.a == 2.5); | ||
| 2725 | zig_struct_f64((struct Struct_f64){ 2.5 }); | ||
| 2726 | } | ||
| 2727 | |||
| 2677 | { | 2728 | { |
| 2678 | struct Struct_f32f32_f32 s = zig_ret_struct_f32f32_f32(); | 2729 | struct Struct_f32f32_f32 s = zig_ret_struct_f32f32_f32(); |
| 2679 | assert_or_panic(s.a.b == 1.0f); | 2730 | assert_or_panic(s.a.b == 1.0f); |
| ... | @@ -2699,6 +2750,10 @@ void run_c_tests(void) { | ... | @@ -2699,6 +2750,10 @@ void run_c_tests(void) { |
| 2699 | assert_or_panic(s.b.c.e == 3); | 2750 | assert_or_panic(s.b.c.e == 3); |
| 2700 | zig_struct_u32_union_u32_u32u32(s); | 2751 | zig_struct_u32_union_u32_u32u32(s); |
| 2701 | } | 2752 | } |
| 2753 | { | ||
| 2754 | struct Struct_i32_i32 s = {1, 2}; | ||
| 2755 | zig_struct_i32_i32(s); | ||
| 2756 | } | ||
| 2702 | #endif | 2757 | #endif |
| 2703 | 2758 | ||
| 2704 | { | 2759 | { |
| ... | @@ -5024,6 +5079,21 @@ double complex c_cmultd(double complex a, double complex b) { | ... | @@ -5024,6 +5079,21 @@ double complex c_cmultd(double complex a, double complex b) { |
| 5024 | return 1.5 + I * 13.5; | 5079 | return 1.5 + I * 13.5; |
| 5025 | } | 5080 | } |
| 5026 | 5081 | ||
| 5082 | struct Struct_i32_i32 c_mut_struct_i32_i32(struct Struct_i32_i32 s) { | ||
| 5083 | assert_or_panic(s.a == 1); | ||
| 5084 | assert_or_panic(s.b == 2); | ||
| 5085 | s.a += 100; | ||
| 5086 | s.b += 250; | ||
| 5087 | assert_or_panic(s.a == 101); | ||
| 5088 | assert_or_panic(s.b == 252); | ||
| 5089 | return s; | ||
| 5090 | } | ||
| 5091 | |||
| 5092 | void c_struct_i32_i32(struct Struct_i32_i32 s) { | ||
| 5093 | assert_or_panic(s.a == 1); | ||
| 5094 | assert_or_panic(s.b == 2); | ||
| 5095 | } | ||
| 5096 | |||
| 5027 | void c_big_struct(struct BigStruct x) { | 5097 | void c_big_struct(struct BigStruct x) { |
| 5028 | assert_or_panic(x.a == 1); | 5098 | assert_or_panic(x.a == 1); |
| 5029 | assert_or_panic(x.b == 2); | 5099 | assert_or_panic(x.b == 2); |
test/c_abi/main.zig+122-52| ... | @@ -13,7 +13,7 @@ const expectEqual = std.testing.expectEqual; | ... | @@ -13,7 +13,7 @@ const expectEqual = std.testing.expectEqual; |
| 13 | const have_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isArm() and | 13 | const have_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isArm() and |
| 14 | !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPowerPC32(); | 14 | !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPowerPC32(); |
| 15 | 15 | ||
| 16 | const have_f128 = builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin(); | 16 | const have_f128 = builtin.cpu.arch.isWasm() or (builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin()); |
| 17 | const have_f80 = builtin.cpu.arch.isX86(); | 17 | const have_f80 = builtin.cpu.arch.isX86(); |
| 18 | 18 | ||
| 19 | extern fn run_c_tests() void; | 19 | extern fn run_c_tests() void; |
| ... | @@ -339,6 +339,56 @@ test "C ABI struct u64 u64" { | ... | @@ -339,6 +339,56 @@ test "C ABI struct u64 u64" { |
| 339 | c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40 }); | 339 | c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40 }); |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | const Struct_f32 = extern struct { | ||
| 343 | a: f32, | ||
| 344 | }; | ||
| 345 | |||
| 346 | export fn zig_ret_struct_f32() Struct_f32 { | ||
| 347 | return .{ .a = 2.5 }; | ||
| 348 | } | ||
| 349 | |||
| 350 | export fn zig_struct_f32(s: Struct_f32) void { | ||
| 351 | expect(s.a == 2.5) catch @panic("test failure"); | ||
| 352 | } | ||
| 353 | |||
| 354 | extern fn c_ret_struct_f32() Struct_f32; | ||
| 355 | |||
| 356 | extern fn c_struct_f32(Struct_f32) void; | ||
| 357 | |||
| 358 | test "C ABI struct f32" { | ||
| 359 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; | ||
| 360 | if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest; | ||
| 361 | |||
| 362 | const s = c_ret_struct_f32(); | ||
| 363 | try expect(s.a == 2.5); | ||
| 364 | c_struct_f32(.{ .a = 2.5 }); | ||
| 365 | } | ||
| 366 | |||
| 367 | const Struct_f64 = extern struct { | ||
| 368 | a: f64, | ||
| 369 | }; | ||
| 370 | |||
| 371 | export fn zig_ret_struct_f64() Struct_f64 { | ||
| 372 | return .{ .a = 2.5 }; | ||
| 373 | } | ||
| 374 | |||
| 375 | export fn zig_struct_f64(s: Struct_f64) void { | ||
| 376 | expect(s.a == 2.5) catch @panic("test failure"); | ||
| 377 | } | ||
| 378 | |||
| 379 | extern fn c_ret_struct_f64() Struct_f64; | ||
| 380 | |||
| 381 | extern fn c_struct_f64(Struct_f64) void; | ||
| 382 | |||
| 383 | test "C ABI struct f64" { | ||
| 384 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; | ||
| 385 | if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest; | ||
| 386 | |||
| 387 | const s = c_ret_struct_f64(); | ||
| 388 | try expect(s.a == 2.5); | ||
| 389 | c_struct_f64(.{ .a = 2.5 }); | ||
| 390 | } | ||
| 391 | |||
| 342 | const Struct_f32f32_f32 = extern struct { | 392 | const Struct_f32f32_f32 = extern struct { |
| 343 | a: extern struct { b: f32, c: f32 }, | 393 | a: extern struct { b: f32, c: f32 }, |
| 344 | d: f32, | 394 | d: f32, |
| ... | @@ -434,6 +484,34 @@ test "C ABI struct{u32,union{u32,struct{u32,u32}}}" { | ... | @@ -434,6 +484,34 @@ test "C ABI struct{u32,union{u32,struct{u32,u32}}}" { |
| 434 | c_struct_u32_union_u32_u32u32(.{ .a = 1, .b = .{ .c = .{ .d = 2, .e = 3 } } }); | 484 | c_struct_u32_union_u32_u32u32(.{ .a = 1, .b = .{ .c = .{ .d = 2, .e = 3 } } }); |
| 435 | } | 485 | } |
| 436 | 486 | ||
| 487 | const Struct_i32_i32 = extern struct { | ||
| 488 | a: i32, | ||
| 489 | b: i32, | ||
| 490 | }; | ||
| 491 | extern fn c_mut_struct_i32_i32(Struct_i32_i32) Struct_i32_i32; | ||
| 492 | extern fn c_struct_i32_i32(Struct_i32_i32) void; | ||
| 493 | |||
| 494 | test "C ABI struct i32 i32" { | ||
| 495 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; | ||
| 496 | if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest; | ||
| 497 | |||
| 498 | const s: Struct_i32_i32 = .{ | ||
| 499 | .a = 1, | ||
| 500 | .b = 2, | ||
| 501 | }; | ||
| 502 | const mut_res = c_mut_struct_i32_i32(s); | ||
| 503 | try expect(s.a == 1); | ||
| 504 | try expect(s.b == 2); | ||
| 505 | try expect(mut_res.a == 101); | ||
| 506 | try expect(mut_res.b == 252); | ||
| 507 | c_struct_i32_i32(s); | ||
| 508 | } | ||
| 509 | |||
| 510 | export fn zig_struct_i32_i32(s: Struct_i32_i32) void { | ||
| 511 | expect(s.a == 1) catch @panic("test failure: zig_struct_i32_i32 1"); | ||
| 512 | expect(s.b == 2) catch @panic("test failure: zig_struct_i32_i32 2"); | ||
| 513 | } | ||
| 514 | |||
| 437 | const BigStruct = extern struct { | 515 | const BigStruct = extern struct { |
| 438 | a: u64, | 516 | a: u64, |
| 439 | b: u64, | 517 | b: u64, |
| ... | @@ -5591,64 +5669,56 @@ test "f80 extra struct" { | ... | @@ -5591,64 +5669,56 @@ test "f80 extra struct" { |
| 5591 | try expect(a.b == 24); | 5669 | try expect(a.b == 24); |
| 5592 | } | 5670 | } |
| 5593 | 5671 | ||
| 5594 | comptime { | 5672 | export fn zig_f128(x: f128) f128 { |
| 5595 | skip: { | 5673 | expect(x == 12) catch @panic("test failure"); |
| 5596 | if (builtin.target.cpu.arch.isWasm()) break :skip; | 5674 | return 34; |
| 5597 | 5675 | } | |
| 5598 | _ = struct { | 5676 | extern fn c_f128(f128) f128; |
| 5599 | export fn zig_f128(x: f128) f128 { | 5677 | test "f128 bare" { |
| 5600 | expect(x == 12) catch @panic("test failure"); | 5678 | if (!have_f128) return error.SkipZigTest; |
| 5601 | return 34; | ||
| 5602 | } | ||
| 5603 | extern fn c_f128(f128) f128; | ||
| 5604 | test "f128 bare" { | ||
| 5605 | if (!have_f128) return error.SkipZigTest; | ||
| 5606 | 5679 | ||
| 5607 | const a = c_f128(12.34); | 5680 | const a = c_f128(12.34); |
| 5608 | try expect(@as(f64, @floatCast(a)) == 56.78); | 5681 | try expect(@as(f64, @floatCast(a)) == 56.78); |
| 5609 | } | 5682 | } |
| 5610 | 5683 | ||
| 5611 | const f128_struct = extern struct { | 5684 | const f128_struct = extern struct { |
| 5612 | a: f128, | 5685 | a: f128, |
| 5613 | }; | 5686 | }; |
| 5614 | export fn zig_f128_struct(a: f128_struct) f128_struct { | 5687 | export fn zig_f128_struct(a: f128_struct) f128_struct { |
| 5615 | expect(a.a == 12345) catch @panic("test failure"); | 5688 | expect(a.a == 12345) catch @panic("test failure"); |
| 5616 | return .{ .a = 98765 }; | 5689 | return .{ .a = 98765 }; |
| 5617 | } | 5690 | } |
| 5618 | extern fn c_f128_struct(f128_struct) f128_struct; | 5691 | extern fn c_f128_struct(f128_struct) f128_struct; |
| 5619 | test "f128 struct" { | 5692 | test "f128 struct" { |
| 5620 | if (!have_f128) return error.SkipZigTest; | 5693 | if (!have_f128) return error.SkipZigTest; |
| 5621 | 5694 | ||
| 5622 | const a = c_f128_struct(.{ .a = 12.34 }); | 5695 | const a = c_f128_struct(.{ .a = 12.34 }); |
| 5623 | try expect(@as(f64, @floatCast(a.a)) == 56.78); | 5696 | try expect(@as(f64, @floatCast(a.a)) == 56.78); |
| 5624 | 5697 | ||
| 5625 | const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 }); | 5698 | const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 }); |
| 5626 | try expect(@as(f64, @floatCast(b.a)) == 56.78); | 5699 | try expect(@as(f64, @floatCast(b.a)) == 56.78); |
| 5627 | try expect(@as(f64, @floatCast(b.b)) == 43.21); | 5700 | try expect(@as(f64, @floatCast(b.b)) == 43.21); |
| 5628 | } | 5701 | } |
| 5629 | 5702 | ||
| 5630 | const f128_f128_struct = extern struct { | 5703 | const f128_f128_struct = extern struct { |
| 5631 | a: f128, | 5704 | a: f128, |
| 5632 | b: f128, | 5705 | b: f128, |
| 5633 | }; | 5706 | }; |
| 5634 | export fn zig_f128_f128_struct(a: f128_f128_struct) f128_f128_struct { | 5707 | export fn zig_f128_f128_struct(a: f128_f128_struct) f128_f128_struct { |
| 5635 | expect(a.a == 13) catch @panic("test failure"); | 5708 | expect(a.a == 13) catch @panic("test failure"); |
| 5636 | expect(a.b == 57) catch @panic("test failure"); | 5709 | expect(a.b == 57) catch @panic("test failure"); |
| 5637 | return .{ .a = 24, .b = 68 }; | 5710 | return .{ .a = 24, .b = 68 }; |
| 5638 | } | 5711 | } |
| 5639 | extern fn c_f128_f128_struct(f128_f128_struct) f128_f128_struct; | 5712 | extern fn c_f128_f128_struct(f128_f128_struct) f128_f128_struct; |
| 5640 | test "f128 f128 struct" { | 5713 | test "f128 f128 struct" { |
| 5641 | if (!have_f128) return error.SkipZigTest; | 5714 | if (!have_f128) return error.SkipZigTest; |
| 5642 | 5715 | ||
| 5643 | const a = c_f128_struct(.{ .a = 12.34 }); | 5716 | const a = c_f128_struct(.{ .a = 12.34 }); |
| 5644 | try expect(@as(f64, @floatCast(a.a)) == 56.78); | 5717 | try expect(@as(f64, @floatCast(a.a)) == 56.78); |
| 5645 | 5718 | ||
| 5646 | const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 }); | 5719 | const b = c_f128_f128_struct(.{ .a = 12.34, .b = 87.65 }); |
| 5647 | try expect(@as(f64, @floatCast(b.a)) == 56.78); | 5720 | try expect(@as(f64, @floatCast(b.a)) == 56.78); |
| 5648 | try expect(@as(f64, @floatCast(b.b)) == 43.21); | 5721 | try expect(@as(f64, @floatCast(b.b)) == 43.21); |
| 5649 | } | ||
| 5650 | }; | ||
| 5651 | } | ||
| 5652 | } | 5722 | } |
| 5653 | 5723 | ||
| 5654 | // The stdcall attribute on C functions is ignored when compiled on non-x86 | 5724 | // The stdcall attribute on C functions is ignored when compiled on non-x86 |