| ... | @@ -483,7 +483,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -483,7 +483,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 483 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, | 483 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, |
| 484 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, | 484 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, |
| 485 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. | 485 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. |
| 486 | .i64 => unreachable, | 486 | .i64 => switch (args.valtype2.?) { |
| | 487 | .i32 => unreachable, |
| | 488 | .i64 => unreachable, |
| | 489 | .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u, |
| | 490 | .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u, |
| | 491 | }, |
| 487 | .f32 => return .f32_trunc, | 492 | .f32 => return .f32_trunc, |
| 488 | .f64 => return .f64_trunc, | 493 | .f64 => return .f64_trunc, |
| 489 | }, | 494 | }, |
| ... | @@ -687,7 +692,10 @@ const InnerError = error{ | ... | @@ -687,7 +692,10 @@ const InnerError = error{ |
| 687 | }; | 692 | }; |
| 688 | | 693 | |
| 689 | pub fn deinit(func: *CodeGen) void { | 694 | pub fn deinit(func: *CodeGen) void { |
| 690 | assert(func.branches.items.len == 0); // we should end with no branches left. Forgot a call to `branches.pop()`? | 695 | // in case of an error and we still have branches |
| | 696 | for (func.branches.items) |*branch| { |
| | 697 | branch.deinit(func.gpa); |
| | 698 | } |
| 691 | func.branches.deinit(func.gpa); | 699 | func.branches.deinit(func.gpa); |
| 692 | func.blocks.deinit(func.gpa); | 700 | func.blocks.deinit(func.gpa); |
| 693 | func.locals.deinit(func.gpa); | 701 | func.locals.deinit(func.gpa); |
| ... | @@ -1317,17 +1325,21 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: | ... | @@ -1317,17 +1325,21 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: |
| 1317 | assert(ty_classes[0] == .direct); | 1325 | assert(ty_classes[0] == .direct); |
| 1318 | const scalar_type = abi.scalarType(ty, func.target); | 1326 | const scalar_type = abi.scalarType(ty, func.target); |
| 1319 | const abi_size = scalar_type.abiSize(func.target); | 1327 | const abi_size = scalar_type.abiSize(func.target); |
| 1320 | const opcode = buildOpcode(.{ | | |
| 1321 | .op = .load, | | |
| 1322 | .width = @intCast(u8, abi_size), | | |
| 1323 | .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned, | | |
| 1324 | .valtype1 = typeToValtype(scalar_type, func.target), | | |
| 1325 | }); | | |
| 1326 | try func.emitWValue(value); | 1328 | try func.emitWValue(value); |
| 1327 | try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ | 1329 | |
| 1328 | .offset = value.offset(), | 1330 | // When the value lives in the virtual stack, we must load it onto the actual stack |
| 1329 | .alignment = scalar_type.abiAlignment(func.target), | 1331 | if (value != .imm32 and value != .imm64) { |
| 1330 | }); | 1332 | const opcode = buildOpcode(.{ |
| | 1333 | .op = .load, |
| | 1334 | .width = @intCast(u8, abi_size), |
| | 1335 | .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned, |
| | 1336 | .valtype1 = typeToValtype(scalar_type, func.target), |
| | 1337 | }); |
| | 1338 | try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ |
| | 1339 | .offset = value.offset(), |
| | 1340 | .alignment = scalar_type.abiAlignment(func.target), |
| | 1341 | }); |
| | 1342 | } |
| 1331 | }, | 1343 | }, |
| 1332 | .Int, .Float => { | 1344 | .Int, .Float => { |
| 1333 | if (ty_classes[1] == .none) { | 1345 | if (ty_classes[1] == .none) { |
| ... | @@ -2478,18 +2490,21 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError | ... | @@ -2478,18 +2490,21 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError |
| 2478 | const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty); | 2490 | const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty); |
| 2479 | | 2491 | |
| 2480 | const offset = switch (parent_ty.zigTypeTag()) { | 2492 | const offset = switch (parent_ty.zigTypeTag()) { |
| 2481 | .Struct => blk: { | 2493 | .Struct => switch (parent_ty.containerLayout()) { |
| 2482 | const offset = parent_ty.structFieldOffset(field_ptr.field_index, func.target); | 2494 | .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target), |
| 2483 | break :blk offset; | 2495 | else => parent_ty.structFieldOffset(field_ptr.field_index, func.target), |
| 2484 | }, | 2496 | }, |
| 2485 | .Union => blk: { | 2497 | .Union => switch (parent_ty.containerLayout()) { |
| 2486 | const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target); | 2498 | .Packed => 0, |
| 2487 | if (layout.payload_size == 0) break :blk 0; | 2499 | else => blk: { |
| 2488 | if (layout.payload_align > layout.tag_align) break :blk 0; | 2500 | const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target); |
| 2489 | | 2501 | if (layout.payload_size == 0) break :blk 0; |
| 2490 | // tag is stored first so calculate offset from where payload starts | 2502 | if (layout.payload_align > layout.tag_align) break :blk 0; |
| 2491 | const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align)); | 2503 | |
| 2492 | break :blk offset; | 2504 | // tag is stored first so calculate offset from where payload starts |
| | 2505 | const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align)); |
| | 2506 | break :blk offset; |
| | 2507 | }, |
| 2493 | }, | 2508 | }, |
| 2494 | .Pointer => switch (parent_ty.ptrSize()) { | 2509 | .Pointer => switch (parent_ty.ptrSize()) { |
| 2495 | .Slice => switch (field_ptr.field_index) { | 2510 | .Slice => switch (field_ptr.field_index) { |
| ... | @@ -2751,6 +2766,11 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -2751,6 +2766,11 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 2751 | .ErrorUnion => { | 2766 | .ErrorUnion => { |
| 2752 | return WValue{ .imm32 = 0xaaaaaaaa }; | 2767 | return WValue{ .imm32 = 0xaaaaaaaa }; |
| 2753 | }, | 2768 | }, |
| | 2769 | .Struct => { |
| | 2770 | const struct_obj = ty.castTag(.@"struct").?.data; |
| | 2771 | assert(struct_obj.layout == .Packed); |
| | 2772 | return func.emitUndefined(struct_obj.backing_int_ty); |
| | 2773 | }, |
| 2754 | else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}), | 2774 | else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}), |
| 2755 | } | 2775 | } |
| 2756 | } | 2776 | } |
| ... | @@ -3201,6 +3221,18 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3201,6 +3221,18 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3201 | const truncated = try func.trunc(shifted_value, int_type, backing_ty); | 3221 | const truncated = try func.trunc(shifted_value, int_type, backing_ty); |
| 3202 | const bitcasted = try func.bitcast(field_ty, int_type, truncated); | 3222 | const bitcasted = try func.bitcast(field_ty, int_type, truncated); |
| 3203 | break :result try bitcasted.toLocal(func, field_ty); | 3223 | break :result try bitcasted.toLocal(func, field_ty); |
| | 3224 | } else if (field_ty.isPtrAtRuntime() and struct_obj.fields.count() == 1) { |
| | 3225 | // In this case we do not have to perform any transformations, |
| | 3226 | // we can simply reuse the operand. |
| | 3227 | break :result func.reuseOperand(struct_field.struct_operand, operand); |
| | 3228 | } else if (field_ty.isPtrAtRuntime()) { |
| | 3229 | var payload: Type.Payload.Bits = .{ |
| | 3230 | .base = .{ .tag = .int_unsigned }, |
| | 3231 | .data = @intCast(u16, field_ty.bitSize(func.target)), |
| | 3232 | }; |
| | 3233 | const int_type = Type.initPayload(&payload.base); |
| | 3234 | const truncated = try func.trunc(shifted_value, int_type, backing_ty); |
| | 3235 | break :result try truncated.toLocal(func, field_ty); |
| 3204 | } | 3236 | } |
| 3205 | const truncated = try func.trunc(shifted_value, field_ty, backing_ty); | 3237 | const truncated = try func.trunc(shifted_value, field_ty, backing_ty); |
| 3206 | break :result try truncated.toLocal(func, field_ty); | 3238 | break :result try truncated.toLocal(func, field_ty); |
| ... | @@ -3225,6 +3257,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3225,6 +3257,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3225 | break :result try field.toLocal(func, field_ty); | 3257 | break :result try field.toLocal(func, field_ty); |
| 3226 | }, | 3258 | }, |
| 3227 | }; | 3259 | }; |
| | 3260 | |
| 3228 | func.finishAir(inst, result, &.{struct_field.struct_operand}); | 3261 | func.finishAir(inst, result, &.{struct_field.struct_operand}); |
| 3229 | } | 3262 | } |
| 3230 | | 3263 | |
| ... | @@ -3571,13 +3604,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3571,13 +3604,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3571 | /// Asserts type's bitsize <= 128 | 3604 | /// Asserts type's bitsize <= 128 |
| 3572 | /// NOTE: May leave the result on the top of the stack. | 3605 | /// NOTE: May leave the result on the top of the stack. |
| 3573 | fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { | 3606 | fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 3574 | const given_info = given.intInfo(func.target); | 3607 | const given_bitsize = @intCast(u16, given.bitSize(func.target)); |
| 3575 | const wanted_info = wanted.intInfo(func.target); | 3608 | const wanted_bitsize = @intCast(u16, wanted.bitSize(func.target)); |
| 3576 | assert(given_info.bits <= 128); | 3609 | assert(given_bitsize <= 128); |
| 3577 | assert(wanted_info.bits <= 128); | 3610 | assert(wanted_bitsize <= 128); |
| 3578 | | 3611 | |
| 3579 | const op_bits = toWasmBits(given_info.bits).?; | 3612 | const op_bits = toWasmBits(given_bitsize).?; |
| 3580 | const wanted_bits = toWasmBits(wanted_info.bits).?; | 3613 | const wanted_bits = toWasmBits(wanted_bitsize).?; |
| 3581 | if (op_bits == wanted_bits) return operand; | 3614 | if (op_bits == wanted_bits) return operand; |
| 3582 | | 3615 | |
| 3583 | if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) { | 3616 | if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) { |
| ... | @@ -3585,10 +3618,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro | ... | @@ -3585,10 +3618,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 3585 | try func.addTag(.i32_wrap_i64); | 3618 | try func.addTag(.i32_wrap_i64); |
| 3586 | } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) { | 3619 | } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) { |
| 3587 | try func.emitWValue(operand); | 3620 | try func.emitWValue(operand); |
| 3588 | try func.addTag(switch (wanted_info.signedness) { | 3621 | try func.addTag(if (wanted.isSignedInt()) .i64_extend_i32_s else .i64_extend_i32_u); |
| 3589 | .signed => .i64_extend_i32_s, | | |
| 3590 | .unsigned => .i64_extend_i32_u, | | |
| 3591 | }); | | |
| 3592 | } else if (wanted_bits == 128) { | 3622 | } else if (wanted_bits == 128) { |
| 3593 | // for 128bit integers we store the integer in the virtual stack, rather than a local | 3623 | // for 128bit integers we store the integer in the virtual stack, rather than a local |
| 3594 | const stack_ptr = try func.allocStack(wanted); | 3624 | const stack_ptr = try func.allocStack(wanted); |
| ... | @@ -3869,7 +3899,7 @@ fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) Inner | ... | @@ -3869,7 +3899,7 @@ fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) Inner |
| 3869 | } | 3899 | } |
| 3870 | | 3900 | |
| 3871 | var result = try func.intcast(operand, given_ty, wanted_ty); | 3901 | var result = try func.intcast(operand, given_ty, wanted_ty); |
| 3872 | const wanted_bits = wanted_ty.intInfo(func.target).bits; | 3902 | const wanted_bits = @intCast(u16, wanted_ty.bitSize(func.target)); |
| 3873 | const wasm_bits = toWasmBits(wanted_bits).?; | 3903 | const wasm_bits = toWasmBits(wanted_bits).?; |
| 3874 | if (wasm_bits != wanted_bits) { | 3904 | if (wasm_bits != wanted_bits) { |
| 3875 | result = try func.wrapOperand(result, wanted_ty); | 3905 | result = try func.wrapOperand(result, wanted_ty); |