| author | |
| committer | |
| log | 200b2e4ee108d388e0db2120aee38f83d1c7abdb |
| tree | 49139a306103997d18ed83624b3fec2c3cb13468 |
| parent | bf28a47cf23a60539f7cc6cb87309d1a7fa01c18 |
| signature | Commit is signed but in an unrecognized format. |
When lowering the return type for Wasm if the calling convention is `C`,
it now correctly lower it according to what clang does as specified in:
https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
This makes use of the same logic as the Wasm backend, ensuring the
generated code does not diverge in function signatures.
When passing arguments accross the C-ABI for the Wasm target,
we want slightly different behavior than x86_64.
For instance: a struct with multiple fields must always be passed
by reference, even if its ABI size fits in a single integer.
However, we do pass larger integers such as 128bit by value,
which LLVM will correctly lower to use double arguments instead.2 files changed, 58 insertions(+), 28 deletions(-)
src/arch/wasm/abi.zig+27-28| ... | @@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { | ... | @@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { |
| 23 | if (!ty.hasRuntimeBitsIgnoreComptime()) return none; | 23 | if (!ty.hasRuntimeBitsIgnoreComptime()) return none; |
| 24 | switch (ty.zigTypeTag()) { | 24 | switch (ty.zigTypeTag()) { |
| 25 | .Struct => { | 25 | .Struct => { |
| 26 | // When the (maybe) scalar type exceeds max 'direct' integer size | ||
| 27 | if (ty.abiSize(target) > 8) return memory; | ||
| 28 | // When the struct type is non-scalar | 26 | // When the struct type is non-scalar |
| 29 | if (ty.structFieldCount() > 1) return memory; | 27 | if (ty.structFieldCount() > 1) return memory; |
| 30 | // When the struct's alignment is non-natural | 28 | // When the struct's alignment is non-natural |
| ... | @@ -34,56 +32,57 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { | ... | @@ -34,56 +32,57 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { |
| 34 | return memory; | 32 | return memory; |
| 35 | } | 33 | } |
| 36 | } | 34 | } |
| 37 | if (field.ty.isInt() or field.ty.isAnyFloat()) { | ||
| 38 | return direct; | ||
| 39 | } | ||
| 40 | return classifyType(field.ty, target); | 35 | return classifyType(field.ty, target); |
| 41 | }, | 36 | }, |
| 42 | .Int, .Enum, .ErrorSet, .Vector => { | 37 | .Int, .Enum, .ErrorSet, .Vector => { |
| 43 | const int_bits = ty.intInfo(target).bits; | 38 | const int_bits = ty.intInfo(target).bits; |
| 44 | if (int_bits <= 64) return direct; | 39 | if (int_bits <= 64) return direct; |
| 45 | if (int_bits > 64 and int_bits <= 128) return .{ .direct, .direct }; | 40 | if (int_bits <= 128) return .{ .direct, .direct }; |
| 46 | return memory; | 41 | return memory; |
| 47 | }, | 42 | }, |
| 48 | .Float => { | 43 | .Float => { |
| 49 | const float_bits = ty.floatBits(target); | 44 | const float_bits = ty.floatBits(target); |
| 50 | if (float_bits <= 64) return direct; | 45 | if (float_bits <= 64) return direct; |
| 51 | if (float_bits > 64 and float_bits <= 128) return .{ .direct, .direct }; | 46 | if (float_bits <= 128) return .{ .direct, .direct }; |
| 52 | return memory; | 47 | return memory; |
| 53 | }, | 48 | }, |
| 54 | .Bool => return direct, | 49 | .Bool => return direct, |
| 55 | .Array => return memory, | 50 | .Array => return memory, |
| 56 | .ErrorUnion => { | 51 | // .ErrorUnion => { |
| 57 | const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); | 52 | // const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); |
| 58 | const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); | 53 | // const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); |
| 59 | if (!has_pl) return direct; | 54 | // if (!has_pl) return direct; |
| 60 | if (!has_tag) { | 55 | // if (!has_tag) { |
| 61 | return classifyType(ty.errorUnionPayload(), target); | 56 | // return classifyType(ty.errorUnionPayload(), target); |
| 62 | } | 57 | // } |
| 63 | return memory; | 58 | // return memory; |
| 64 | }, | 59 | // }, |
| 65 | .Optional => { | 60 | .Optional => { |
| 66 | if (ty.isPtrLikeOptional()) return direct; | 61 | std.debug.assert(ty.isPtrLikeOptional()); |
| 67 | var buf: Type.Payload.ElemType = undefined; | 62 | return direct; |
| 68 | const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); | 63 | // var buf: Type.Payload.ElemType = undefined; |
| 69 | if (!pl_has_bits) return direct; | 64 | // const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); |
| 70 | return memory; | 65 | // if (!pl_has_bits) return direct; |
| 66 | // return memory; | ||
| 71 | }, | 67 | }, |
| 72 | .Pointer => { | 68 | .Pointer => { |
| 73 | // Slices act like struct and will be passed by reference | 69 | // // Slices act like struct and will be passed by reference |
| 74 | if (ty.isSlice()) return memory; | 70 | // if (ty.isSlice()) return memory; |
| 75 | return direct; | 71 | return direct; |
| 76 | }, | 72 | }, |
| 77 | .Union => { | 73 | .Union => { |
| 78 | const layout = ty.unionGetLayout(target); | 74 | const layout = ty.unionGetLayout(target); |
| 79 | if (layout.payload_size == 0 and layout.tag_size != 0) { | 75 | std.debug.assert(layout.tag_size == 0); |
| 80 | return classifyType(ty.unionTagTypeSafety().?, target); | 76 | // if (layout.payload_size == 0 and layout.tag_size != 0) { |
| 81 | } | 77 | // return classifyType(ty.unionTagType().?, target); |
| 78 | // } | ||
| 82 | if (ty.unionFields().count() > 1) return memory; | 79 | if (ty.unionFields().count() > 1) return memory; |
| 83 | return classifyType(ty.unionFields().values()[0].ty, target); | 80 | return classifyType(ty.unionFields().values()[0].ty, target); |
| 84 | }, | 81 | }, |
| 85 | .AnyFrame, .Frame => return direct, | 82 | // .AnyFrame, .Frame => return direct, |
| 86 | 83 | .ErrorUnion, | |
| 84 | .Frame, | ||
| 85 | .AnyFrame, | ||
| 87 | .NoReturn, | 86 | .NoReturn, |
| 88 | .Void, | 87 | .Void, |
| 89 | .Type, | 88 | .Type, |
src/codegen/llvm.zig+31| ... | @@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type; | ... | @@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type; |
| 22 | const LazySrcLoc = Module.LazySrcLoc; | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | const CType = @import("../type.zig").CType; | 23 | const CType = @import("../type.zig").CType; |
| 24 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); | 24 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 25 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); | ||
| 25 | 26 | ||
| 26 | const Error = error{ OutOfMemory, CodegenFail }; | 27 | const Error = error{ OutOfMemory, CodegenFail }; |
| 27 | 28 | ||
| ... | @@ -9093,6 +9094,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool | ... | @@ -9093,6 +9094,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool |
| 9093 | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, | 9094 | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, |
| 9094 | else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, | 9095 | else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, |
| 9095 | }, | 9096 | }, |
| 9097 | .wasm32 => { | ||
| 9098 | const classes = wasm_c_abi.classifyType(fn_info.return_type, target); | ||
| 9099 | return classes[0] == .indirect; | ||
| 9100 | }, | ||
| 9096 | else => return false, // TODO investigate C ABI for other architectures | 9101 | else => return false, // TODO investigate C ABI for other architectures |
| 9097 | }, | 9102 | }, |
| 9098 | else => return false, | 9103 | else => return false, |
| ... | @@ -9197,6 +9202,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. | ... | @@ -9197,6 +9202,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 9197 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); | 9202 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 9198 | }, | 9203 | }, |
| 9199 | }, | 9204 | }, |
| 9205 | .wasm32 => { | ||
| 9206 | if (is_scalar) { | ||
| 9207 | return dg.lowerType(fn_info.return_type); | ||
| 9208 | } | ||
| 9209 | const classes = wasm_c_abi.classifyType(fn_info.return_type, target); | ||
| 9210 | if (classes[0] == .indirect or classes[0] == .none) { | ||
| 9211 | return dg.context.voidType(); | ||
| 9212 | } | ||
| 9213 | |||
| 9214 | assert(classes[0] == .direct and classes[1] == .none); | ||
| 9215 | const scalar_type = wasm_c_abi.scalarType(fn_info.return_type, target); | ||
| 9216 | const abi_size = scalar_type.abiSize(target); | ||
| 9217 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); | ||
| 9218 | }, | ||
| 9200 | // TODO investigate C ABI for other architectures | 9219 | // TODO investigate C ABI for other architectures |
| 9201 | else => return dg.lowerType(fn_info.return_type), | 9220 | else => return dg.lowerType(fn_info.return_type), |
| 9202 | } | 9221 | } |
| ... | @@ -9372,6 +9391,18 @@ const ParamTypeIterator = struct { | ... | @@ -9372,6 +9391,18 @@ const ParamTypeIterator = struct { |
| 9372 | return .multiple_llvm_ints; | 9391 | return .multiple_llvm_ints; |
| 9373 | }, | 9392 | }, |
| 9374 | }, | 9393 | }, |
| 9394 | .wasm32 => { | ||
| 9395 | it.zig_index += 1; | ||
| 9396 | it.llvm_index += 1; | ||
| 9397 | if (is_scalar) { | ||
| 9398 | return .byval; | ||
| 9399 | } | ||
| 9400 | const classes = wasm_c_abi.classifyType(ty, it.target); | ||
| 9401 | if (classes[0] == .indirect) { | ||
| 9402 | return .byref; | ||
| 9403 | } | ||
| 9404 | return .abi_sized_int; | ||
| 9405 | }, | ||
| 9375 | // TODO investigate C ABI for other architectures | 9406 | // TODO investigate C ABI for other architectures |
| 9376 | else => { | 9407 | else => { |
| 9377 | it.zig_index += 1; | 9408 | it.zig_index += 1; |