| author | |
| committer | |
| log | 331bd83f1115bf5e33b6b2d7651d67dcfe16ac2a |
| tree | cfb5b8ae47de4f0276c5c5f7ec4bf13f487da078 |
| parent | 0209c68fcca22ffe1ab359096f6f3aeb477043e2 |
| signature |
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| ... | @@ -1398,11 +1398,22 @@ fn resolveCallingConventionValues( | ... | @@ -1398,11 +1398,22 @@ fn resolveCallingConventionValues( |
| 1398 | }, | 1398 | }, |
| 1399 | .wasm_mvp => { | 1399 | .wasm_mvp => { |
| 1400 | for (fn_info.param_types.get(ip)) |ty| { | 1400 | for (fn_info.param_types.get(ip)) |ty| { |
| 1401 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); | 1401 | if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) { |
| 1402 | for (ty_classes) |class| { | 1402 | continue; |
| 1403 | if (class == .none) continue; | 1403 | } |
| 1404 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | 1404 | switch (abi.classifyType(.fromInterned(ty), zcu)) { |
| 1405 | result.local_index += 1; | 1405 | .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 1406 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1407 | result.local_index += 1; | ||
| 1408 | } else { | ||
| 1409 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1410 | try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } }); | ||
| 1411 | result.local_index += 2; | ||
| 1412 | }, | ||
| 1413 | .indirect => { | ||
| 1414 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | ||
| 1415 | result.local_index += 1; | ||
| 1416 | }, | ||
| 1406 | } | 1417 | } |
| 1407 | } | 1418 | } |
| 1408 | }, | 1419 | }, |
| ... | @@ -1418,14 +1429,13 @@ pub fn firstParamSRet( | ... | @@ -1418,14 +1429,13 @@ pub fn firstParamSRet( |
| 1418 | zcu: *const Zcu, | 1429 | zcu: *const Zcu, |
| 1419 | target: *const std.Target, | 1430 | target: *const std.Target, |
| 1420 | ) bool { | 1431 | ) bool { |
| 1432 | if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false; | ||
| 1421 | switch (cc) { | 1433 | switch (cc) { |
| 1422 | .@"inline" => unreachable, | 1434 | .@"inline" => unreachable, |
| 1423 | .auto => return isByRef(return_type, zcu, target), | 1435 | .auto => return isByRef(return_type, zcu, target), |
| 1424 | .wasm_mvp => { | 1436 | .wasm_mvp => switch (abi.classifyType(return_type, zcu)) { |
| 1425 | const ty_classes = abi.classifyType(return_type, zcu); | 1437 | .direct => |scalar_ty| return abi.lowerAsDoubleI64(scalar_ty, zcu), |
| 1426 | if (ty_classes[0] == .indirect) return true; | 1438 | .indirect => return true, |
| 1427 | if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; | ||
| 1428 | return false; | ||
| 1429 | }, | 1439 | }, |
| 1430 | else => return false, | 1440 | else => return false, |
| 1431 | } | 1441 | } |
| ... | @@ -1439,26 +1449,19 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV | ... | @@ -1439,26 +1449,19 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV |
| 1439 | } | 1449 | } |
| 1440 | 1450 | ||
| 1441 | const zcu = cg.pt.zcu; | 1451 | const zcu = cg.pt.zcu; |
| 1442 | const ty_classes = abi.classifyType(ty, zcu); | 1452 | |
| 1443 | assert(ty_classes[0] != .none); | 1453 | switch (abi.classifyType(ty, zcu)) { |
| 1444 | switch (ty.zigTypeTag(zcu)) { | 1454 | .direct => |scalar_type| if (!abi.lowerAsDoubleI64(scalar_type, zcu)) { |
| 1445 | .@"struct", .@"union" => { | 1455 | if (!isByRef(ty, zcu, cg.target)) { |
| 1446 | if (ty_classes[0] == .indirect) { | ||
| 1447 | return cg.lowerToStack(value); | ||
| 1448 | } | ||
| 1449 | assert(ty_classes[0] == .direct); | ||
| 1450 | const scalar_type = abi.scalarType(ty, zcu); | ||
| 1451 | switch (value) { | ||
| 1452 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), | ||
| 1453 | .dead => unreachable, | ||
| 1454 | else => try cg.emitWValue(value), | ||
| 1455 | } | ||
| 1456 | }, | ||
| 1457 | .int, .float => { | ||
| 1458 | if (ty_classes[1] == .none) { | ||
| 1459 | return cg.lowerToStack(value); | 1456 | return cg.lowerToStack(value); |
| 1457 | } else { | ||
| 1458 | switch (value) { | ||
| 1459 | .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0), | ||
| 1460 | .dead => unreachable, | ||
| 1461 | else => try cg.emitWValue(value), | ||
| 1462 | } | ||
| 1460 | } | 1463 | } |
| 1461 | assert(ty_classes[0] == .direct and ty_classes[1] == .direct); | 1464 | } else { |
| 1462 | assert(ty.abiSize(zcu) == 16); | 1465 | assert(ty.abiSize(zcu) == 16); |
| 1463 | // in this case we have an integer or float that must be lowered as 2 i64's. | 1466 | // in this case we have an integer or float that must be lowered as 2 i64's. |
| 1464 | try cg.emitWValue(value); | 1467 | try cg.emitWValue(value); |
| ... | @@ -1466,7 +1469,7 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV | ... | @@ -1466,7 +1469,7 @@ fn lowerArg(cg: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WV |
| 1466 | try cg.emitWValue(value); | 1469 | try cg.emitWValue(value); |
| 1467 | try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); | 1470 | try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); |
| 1468 | }, | 1471 | }, |
| 1469 | else => return cg.lowerToStack(value), | 1472 | .indirect => return cg.lowerToStack(value), |
| 1470 | } | 1473 | } |
| 1471 | } | 1474 | } |
| 1472 | 1475 | ||
| ... | @@ -2125,23 +2128,16 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2125,23 +2128,16 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2125 | if (cg.return_value != .none) { | 2128 | if (cg.return_value != .none) { |
| 2126 | try cg.store(cg.return_value, operand, ret_ty, 0); | 2129 | try cg.store(cg.return_value, operand, ret_ty, 0); |
| 2127 | } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 2130 | } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 2128 | switch (ret_ty.zigTypeTag(zcu)) { | 2131 | switch (abi.classifyType(ret_ty, zcu)) { |
| 2129 | // Aggregate types can be lowered as a singular value | 2132 | .direct => |scalar_type| { |
| 2130 | .@"struct", .@"union" => { | 2133 | assert(!abi.lowerAsDoubleI64(scalar_type, zcu)); |
| 2131 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2134 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2132 | try cg.emitWValue(operand); | 2135 | try cg.emitWValue(operand); |
| 2133 | const opcode = buildOpcode(.{ | 2136 | } else { |
| 2134 | .op = .load, | 2137 | _ = try cg.load(operand, scalar_type, 0); |
| 2135 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), | 2138 | } |
| 2136 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, | ||
| 2137 | .valtype1 = typeToValtype(scalar_type, zcu, cg.target), | ||
| 2138 | }); | ||
| 2139 | try cg.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ | ||
| 2140 | .offset = operand.offset(), | ||
| 2141 | .alignment = @intCast(scalar_type.abiAlignment(zcu).toByteUnits().?), | ||
| 2142 | }); | ||
| 2143 | }, | 2139 | }, |
| 2144 | else => try cg.emitWValue(operand), | 2140 | .indirect => unreachable, |
| 2145 | } | 2141 | } |
| 2146 | } else { | 2142 | } else { |
| 2147 | if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { | 2143 | 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 | ... | @@ -2267,14 +2263,24 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie |
| 2267 | break :result_value .none; | 2263 | break :result_value .none; |
| 2268 | } else if (first_param_sret) { | 2264 | } else if (first_param_sret) { |
| 2269 | break :result_value sret; | 2265 | break :result_value sret; |
| 2270 | // TODO: Make this less fragile and optimize | 2266 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp) { |
| 2271 | } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp and ret_ty.zigTypeTag(zcu) == .@"struct" or ret_ty.zigTypeTag(zcu) == .@"union") { | 2267 | switch (abi.classifyType(ret_ty, zcu)) { |
| 2272 | const result_local = try cg.allocLocal(ret_ty); | 2268 | .direct => |scalar_type| { |
| 2273 | try cg.addLocal(.local_set, result_local.local.value); | 2269 | assert(!abi.lowerAsDoubleI64(scalar_type, zcu)); |
| 2274 | const scalar_type = abi.scalarType(ret_ty, zcu); | 2270 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2275 | const result = try cg.allocStack(scalar_type); | 2271 | const result_local = try cg.allocLocal(ret_ty); |
| 2276 | try cg.store(result, result_local, scalar_type, 0); | 2272 | try cg.addLocal(.local_set, result_local.local.value); |
| 2277 | break :result_value result; | 2273 | break :result_value result_local; |
| 2274 | } else { | ||
| 2275 | const result_local = try cg.allocLocal(ret_ty); | ||
| 2276 | try cg.addLocal(.local_set, result_local.local.value); | ||
| 2277 | const result = try cg.allocStack(ret_ty); | ||
| 2278 | try cg.store(result, result_local, scalar_type, 0); | ||
| 2279 | break :result_value result; | ||
| 2280 | } | ||
| 2281 | }, | ||
| 2282 | .indirect => unreachable, | ||
| 2283 | } | ||
| 2278 | } else { | 2284 | } else { |
| 2279 | const result_local = try cg.allocLocal(ret_ty); | 2285 | const result_local = try cg.allocLocal(ret_ty); |
| 2280 | try cg.addLocal(.local_set, result_local.local.value); | 2286 | try cg.addLocal(.local_set, result_local.local.value); |
| ... | @@ -2547,26 +2553,17 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2547,26 +2553,17 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2547 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; | 2553 | const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc; |
| 2548 | const arg_ty = cg.typeOfIndex(inst); | 2554 | const arg_ty = cg.typeOfIndex(inst); |
| 2549 | if (cc == .wasm_mvp) { | 2555 | if (cc == .wasm_mvp) { |
| 2550 | const arg_classes = abi.classifyType(arg_ty, zcu); | 2556 | switch (abi.classifyType(arg_ty, zcu)) { |
| 2551 | for (arg_classes) |class| { | 2557 | .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 2552 | if (class != .none) { | ||
| 2553 | cg.arg_index += 1; | 2558 | cg.arg_index += 1; |
| 2554 | } | 2559 | } else { |
| 2555 | } | 2560 | cg.arg_index += 2; |
| 2556 | 2561 | const result = try cg.allocStack(arg_ty); | |
| 2557 | // When we have an argument that's passed using more than a single parameter, | 2562 | try cg.store(result, arg, Type.u64, 0); |
| 2558 | // we combine them into a single stack value | 2563 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); |
| 2559 | if (arg_classes[0] == .direct and arg_classes[1] == .direct) { | 2564 | return cg.finishAir(inst, result, &.{}); |
| 2560 | if (arg_ty.zigTypeTag(zcu) != .int and arg_ty.zigTypeTag(zcu) != .float) { | 2565 | }, |
| 2561 | return cg.fail( | 2566 | .indirect => cg.arg_index += 1, |
| 2562 | "TODO: Implement C-ABI argument for type '{}'", | ||
| 2563 | .{arg_ty.fmt(pt)}, | ||
| 2564 | ); | ||
| 2565 | } | ||
| 2566 | const result = try cg.allocStack(arg_ty); | ||
| 2567 | try cg.store(result, arg, Type.u64, 0); | ||
| 2568 | try cg.store(result, cg.args[arg_index + 1], Type.u64, 8); | ||
| 2569 | return cg.finishAir(inst, result, &.{}); | ||
| 2570 | } | 2567 | } |
| 2571 | } else { | 2568 | } else { |
| 2572 | cg.arg_index += 1; | 2569 | 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| ... | @@ -12094,7 +12094,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe | ... | @@ -12094,7 +12094,7 @@ fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Targe |
| 12094 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory, | 12094 | .x86_64_win => x86_64_abi.classifyWindows(return_type, zcu) == .memory, |
| 12095 | .x86_sysv, .x86_win => isByRef(return_type, zcu), | 12095 | .x86_sysv, .x86_win => isByRef(return_type, zcu), |
| 12096 | .x86_stdcall => !isScalar(zcu, return_type), | 12096 | .x86_stdcall => !isScalar(zcu, return_type), |
| 12097 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu)[0] == .indirect, | 12097 | .wasm_mvp => wasm_c_abi.classifyType(return_type, zcu) == .indirect, |
| 12098 | .aarch64_aapcs, | 12098 | .aarch64_aapcs, |
| 12099 | .aarch64_aapcs_darwin, | 12099 | .aarch64_aapcs_darwin, |
| 12100 | .aarch64_aapcs_win, | 12100 | .aarch64_aapcs_win, |
| ... | @@ -12179,18 +12179,9 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu | ... | @@ -12179,18 +12179,9 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu |
| 12179 | return o.builder.structType(.normal, types[0..types_len]); | 12179 | return o.builder.structType(.normal, types[0..types_len]); |
| 12180 | }, | 12180 | }, |
| 12181 | }, | 12181 | }, |
| 12182 | .wasm_mvp => { | 12182 | .wasm_mvp => switch (wasm_c_abi.classifyType(return_type, zcu)) { |
| 12183 | if (isScalar(zcu, return_type)) { | 12183 | .direct => |scalar_ty| return o.lowerType(scalar_ty), |
| 12184 | return o.lowerType(return_type); | 12184 | .indirect => return .void, |
| 12185 | } | ||
| 12186 | const classes = wasm_c_abi.classifyType(return_type, zcu); | ||
| 12187 | if (classes[0] == .indirect or classes[0] == .none) { | ||
| 12188 | return .void; | ||
| 12189 | } | ||
| 12190 | |||
| 12191 | assert(classes[0] == .direct and classes[1] == .none); | ||
| 12192 | const scalar_type = wasm_c_abi.scalarType(return_type, zcu); | ||
| 12193 | return o.builder.intType(@intCast(scalar_type.abiSize(zcu) * 8)); | ||
| 12194 | }, | 12185 | }, |
| 12195 | // TODO investigate other callconvs | 12186 | // TODO investigate other callconvs |
| 12196 | else => return o.lowerType(return_type), | 12187 | else => return o.lowerType(return_type), |
| ... | @@ -12444,17 +12435,28 @@ const ParamTypeIterator = struct { | ... | @@ -12444,17 +12435,28 @@ const ParamTypeIterator = struct { |
| 12444 | }, | 12435 | }, |
| 12445 | } | 12436 | } |
| 12446 | }, | 12437 | }, |
| 12447 | .wasm_mvp => { | 12438 | .wasm_mvp => switch (wasm_c_abi.classifyType(ty, zcu)) { |
| 12448 | it.zig_index += 1; | 12439 | .direct => |scalar_ty| { |
| 12449 | it.llvm_index += 1; | 12440 | if (isScalar(zcu, ty)) { |
| 12450 | if (isScalar(zcu, ty)) { | 12441 | it.zig_index += 1; |
| 12451 | return .byval; | 12442 | it.llvm_index += 1; |
| 12452 | } | 12443 | return .byval; |
| 12453 | const classes = wasm_c_abi.classifyType(ty, zcu); | 12444 | } else { |
| 12454 | if (classes[0] == .indirect) { | 12445 | var types_buffer: [8]Builder.Type = undefined; |
| 12446 | types_buffer[0] = try it.object.lowerType(scalar_ty); | ||
| 12447 | it.types_buffer = types_buffer; | ||
| 12448 | it.types_len = 1; | ||
| 12449 | it.llvm_index += 1; | ||
| 12450 | it.zig_index += 1; | ||
| 12451 | return .multiple_llvm_types; | ||
| 12452 | } | ||
| 12453 | }, | ||
| 12454 | .indirect => { | ||
| 12455 | it.zig_index += 1; | ||
| 12456 | it.llvm_index += 1; | ||
| 12457 | it.byval_attr = true; | ||
| 12455 | return .byref; | 12458 | return .byref; |
| 12456 | } | 12459 | }, |
| 12457 | return .abi_sized_int; | ||
| 12458 | }, | 12460 | }, |
| 12459 | // TODO investigate other callconvs | 12461 | // TODO investigate other callconvs |
| 12460 | else => { | 12462 | else => { |
src/link/Wasm.zig+17-16| ... | @@ -4617,10 +4617,13 @@ fn convertZcuFnType( | ... | @@ -4617,10 +4617,13 @@ fn convertZcuFnType( |
| 4617 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle | 4617 | try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle |
| 4618 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { | 4618 | } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4619 | if (cc == .wasm_mvp) { | 4619 | if (cc == .wasm_mvp) { |
| 4620 | const res_classes = abi.classifyType(return_type, zcu); | 4620 | switch (abi.classifyType(return_type, zcu)) { |
| 4621 | assert(res_classes[0] == .direct and res_classes[1] == .none); | 4621 | .direct => |scalar_ty| { |
| 4622 | const scalar_type = abi.scalarType(return_type, zcu); | 4622 | assert(!abi.lowerAsDoubleI64(scalar_ty, zcu)); |
| 4623 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | 4623 | try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target)); |
| 4624 | }, | ||
| 4625 | .indirect => unreachable, | ||
| 4626 | } | ||
| 4624 | } else { | 4627 | } else { |
| 4625 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); | 4628 | try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target)); |
| 4626 | } | 4629 | } |
| ... | @@ -4635,18 +4638,16 @@ fn convertZcuFnType( | ... | @@ -4635,18 +4638,16 @@ fn convertZcuFnType( |
| 4635 | 4638 | ||
| 4636 | switch (cc) { | 4639 | switch (cc) { |
| 4637 | .wasm_mvp => { | 4640 | .wasm_mvp => { |
| 4638 | const param_classes = abi.classifyType(param_type, zcu); | 4641 | switch (abi.classifyType(param_type, zcu)) { |
| 4639 | if (param_classes[1] == .none) { | 4642 | .direct => |scalar_ty| { |
| 4640 | if (param_classes[0] == .direct) { | 4643 | if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) { |
| 4641 | const scalar_type = abi.scalarType(param_type, zcu); | 4644 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target)); |
| 4642 | try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target)); | 4645 | } else { |
| 4643 | } else { | 4646 | try params_buffer.append(gpa, .i64); |
| 4644 | try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)); | 4647 | try params_buffer.append(gpa, .i64); |
| 4645 | } | 4648 | } |
| 4646 | } else { | 4649 | }, |
| 4647 | // i128/f128 | 4650 | .indirect => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), |
| 4648 | try params_buffer.append(gpa, .i64); | ||
| 4649 | try params_buffer.append(gpa, .i64); | ||
| 4650 | } | 4651 | } |
| 4651 | }, | 4652 | }, |
| 4652 | else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)), | 4653 | 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 |