| ... | @@ -7,6 +7,7 @@ const leb = std.leb; | ... | @@ -7,6 +7,7 @@ const leb = std.leb; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const log = std.log.scoped(.codegen); | 8 | const log = std.log.scoped(.codegen); |
| 9 | | 9 | |
| | 10 | const CodeGen = @This(); |
| 10 | const codegen = @import("../../codegen.zig"); | 11 | const codegen = @import("../../codegen.zig"); |
| 11 | const Zcu = @import("../../Zcu.zig"); | 12 | const Zcu = @import("../../Zcu.zig"); |
| 12 | const InternPool = @import("../../InternPool.zig"); | 13 | const InternPool = @import("../../InternPool.zig"); |
| ... | @@ -24,6 +25,98 @@ const abi = @import("abi.zig"); | ... | @@ -24,6 +25,98 @@ const abi = @import("abi.zig"); |
| 24 | const Alignment = InternPool.Alignment; | 25 | const Alignment = InternPool.Alignment; |
| 25 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 26 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 26 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 27 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| | 28 | const Wasm = link.File.Wasm; |
| | 29 | |
| | 30 | /// Reference to the function declaration the code |
| | 31 | /// section belongs to |
| | 32 | owner_nav: InternPool.Nav.Index, |
| | 33 | /// Current block depth. Used to calculate the relative difference between a break |
| | 34 | /// and block |
| | 35 | block_depth: u32 = 0, |
| | 36 | air: Air, |
| | 37 | liveness: Liveness, |
| | 38 | gpa: mem.Allocator, |
| | 39 | func_index: InternPool.Index, |
| | 40 | /// Contains a list of current branches. |
| | 41 | /// When we return from a branch, the branch will be popped from this list, |
| | 42 | /// which means branches can only contain references from within its own branch, |
| | 43 | /// or a branch higher (lower index) in the tree. |
| | 44 | branches: std.ArrayListUnmanaged(Branch) = .empty, |
| | 45 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| | 46 | // values: ValueTable, |
| | 47 | /// Mapping from Air.Inst.Index to block ids |
| | 48 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { |
| | 49 | label: u32, |
| | 50 | value: WValue, |
| | 51 | }) = .{}, |
| | 52 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. |
| | 53 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, |
| | 54 | /// The index the next local generated will have |
| | 55 | /// NOTE: arguments share the index with locals therefore the first variable |
| | 56 | /// will have the index that comes after the last argument's index |
| | 57 | local_index: u32, |
| | 58 | /// The index of the current argument. |
| | 59 | /// Used to track which argument is being referenced in `airArg`. |
| | 60 | arg_index: u32 = 0, |
| | 61 | /// List of simd128 immediates. Each value is stored as an array of bytes. |
| | 62 | /// This list will only be populated for 128bit-simd values when the target features |
| | 63 | /// are enabled also. |
| | 64 | simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, |
| | 65 | /// The Target we're emitting (used to call intInfo) |
| | 66 | target: *const std.Target, |
| | 67 | wasm: *link.File.Wasm, |
| | 68 | pt: Zcu.PerThread, |
| | 69 | /// List of MIR Instructions |
| | 70 | mir_instructions: *std.MultiArrayList(Mir.Inst), |
| | 71 | /// Contains extra data for MIR |
| | 72 | mir_extra: *std.ArrayListUnmanaged(u32), |
| | 73 | /// List of all locals' types generated throughout this declaration |
| | 74 | /// used to emit locals count at start of 'code' section. |
| | 75 | locals: *std.ArrayListUnmanaged(u8), |
| | 76 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| | 77 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| | 78 | initial_stack_value: WValue = .none, |
| | 79 | /// The current stack pointer subtracted with the stack size. From this value, we will calculate |
| | 80 | /// all offsets of the stack values. |
| | 81 | bottom_stack_value: WValue = .none, |
| | 82 | /// Arguments of this function declaration |
| | 83 | /// This will be set after `resolveCallingConventionValues` |
| | 84 | args: []WValue, |
| | 85 | /// This will only be `.none` if the function returns void, or returns an immediate. |
| | 86 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| | 87 | /// before this function returns its execution to the caller. |
| | 88 | return_value: WValue, |
| | 89 | /// The size of the stack this function occupies. In the function prologue |
| | 90 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. |
| | 91 | stack_size: u32 = 0, |
| | 92 | /// The stack alignment, which is 16 bytes by default. This is specified by the |
| | 93 | /// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md |
| | 94 | /// and also what the llvm backend will emit. |
| | 95 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. |
| | 96 | stack_alignment: Alignment = .@"16", |
| | 97 | |
| | 98 | // For each individual Wasm valtype we store a seperate free list which |
| | 99 | // allows us to re-use locals that are no longer used. e.g. a temporary local. |
| | 100 | /// A list of indexes which represents a local of valtype `i32`. |
| | 101 | /// It is illegal to store a non-i32 valtype in this list. |
| | 102 | free_locals_i32: std.ArrayListUnmanaged(u32) = .empty, |
| | 103 | /// A list of indexes which represents a local of valtype `i64`. |
| | 104 | /// It is illegal to store a non-i64 valtype in this list. |
| | 105 | free_locals_i64: std.ArrayListUnmanaged(u32) = .empty, |
| | 106 | /// A list of indexes which represents a local of valtype `f32`. |
| | 107 | /// It is illegal to store a non-f32 valtype in this list. |
| | 108 | free_locals_f32: std.ArrayListUnmanaged(u32) = .empty, |
| | 109 | /// A list of indexes which represents a local of valtype `f64`. |
| | 110 | /// It is illegal to store a non-f64 valtype in this list. |
| | 111 | free_locals_f64: std.ArrayListUnmanaged(u32) = .empty, |
| | 112 | /// A list of indexes which represents a local of valtype `v127`. |
| | 113 | /// It is illegal to store a non-v128 valtype in this list. |
| | 114 | free_locals_v128: std.ArrayListUnmanaged(u32) = .empty, |
| | 115 | |
| | 116 | /// When in debug mode, this tracks if no `finishAir` was missed. |
| | 117 | /// Forgetting to call `finishAir` will cause the result to not be |
| | 118 | /// stored in our `values` map and therefore cause bugs. |
| | 119 | air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init, |
| 27 | | 120 | |
| 28 | /// Wasm Value, created when generating an instruction | 121 | /// Wasm Value, created when generating an instruction |
| 29 | const WValue = union(enum) { | 122 | const WValue = union(enum) { |
| ... | @@ -601,104 +694,6 @@ test "Wasm - buildOpcode" { | ... | @@ -601,104 +694,6 @@ test "Wasm - buildOpcode" { |
| 601 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` | 694 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 602 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); | 695 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 603 | | 696 | |
| 604 | const CodeGen = @This(); | | |
| 605 | | | |
| 606 | /// Reference to the function declaration the code | | |
| 607 | /// section belongs to | | |
| 608 | owner_nav: InternPool.Nav.Index, | | |
| 609 | src_loc: Zcu.LazySrcLoc, | | |
| 610 | /// Current block depth. Used to calculate the relative difference between a break | | |
| 611 | /// and block | | |
| 612 | block_depth: u32 = 0, | | |
| 613 | air: Air, | | |
| 614 | liveness: Liveness, | | |
| 615 | gpa: mem.Allocator, | | |
| 616 | debug_output: link.File.DebugInfoOutput, | | |
| 617 | func_index: InternPool.Index, | | |
| 618 | /// Contains a list of current branches. | | |
| 619 | /// When we return from a branch, the branch will be popped from this list, | | |
| 620 | /// which means branches can only contain references from within its own branch, | | |
| 621 | /// or a branch higher (lower index) in the tree. | | |
| 622 | branches: std.ArrayListUnmanaged(Branch) = .empty, | | |
| 623 | /// Table to save `WValue`'s generated by an `Air.Inst` | | |
| 624 | // values: ValueTable, | | |
| 625 | /// Mapping from Air.Inst.Index to block ids | | |
| 626 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { | | |
| 627 | label: u32, | | |
| 628 | value: WValue, | | |
| 629 | }) = .{}, | | |
| 630 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. | | |
| 631 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, | | |
| 632 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. | | |
| 633 | code: *std.ArrayListUnmanaged(u8), | | |
| 634 | /// The index the next local generated will have | | |
| 635 | /// NOTE: arguments share the index with locals therefore the first variable | | |
| 636 | /// will have the index that comes after the last argument's index | | |
| 637 | local_index: u32 = 0, | | |
| 638 | /// The index of the current argument. | | |
| 639 | /// Used to track which argument is being referenced in `airArg`. | | |
| 640 | arg_index: u32 = 0, | | |
| 641 | /// List of all locals' types generated throughout this declaration | | |
| 642 | /// used to emit locals count at start of 'code' section. | | |
| 643 | locals: std.ArrayListUnmanaged(u8), | | |
| 644 | /// List of simd128 immediates. Each value is stored as an array of bytes. | | |
| 645 | /// This list will only be populated for 128bit-simd values when the target features | | |
| 646 | /// are enabled also. | | |
| 647 | simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, | | |
| 648 | /// The Target we're emitting (used to call intInfo) | | |
| 649 | target: *const std.Target, | | |
| 650 | /// Represents the wasm binary file that is being linked. | | |
| 651 | bin_file: *link.File.Wasm, | | |
| 652 | pt: Zcu.PerThread, | | |
| 653 | /// List of MIR Instructions | | |
| 654 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | | |
| 655 | /// Contains extra data for MIR | | |
| 656 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | | |
| 657 | /// When a function is executing, we store the the current stack pointer's value within this local. | | |
| 658 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | | |
| 659 | initial_stack_value: WValue = .none, | | |
| 660 | /// The current stack pointer subtracted with the stack size. From this value, we will calculate | | |
| 661 | /// all offsets of the stack values. | | |
| 662 | bottom_stack_value: WValue = .none, | | |
| 663 | /// Arguments of this function declaration | | |
| 664 | /// This will be set after `resolveCallingConventionValues` | | |
| 665 | args: []WValue = &.{}, | | |
| 666 | /// This will only be `.none` if the function returns void, or returns an immediate. | | |
| 667 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated | | |
| 668 | /// before this function returns its execution to the caller. | | |
| 669 | return_value: WValue = .none, | | |
| 670 | /// The size of the stack this function occupies. In the function prologue | | |
| 671 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. | | |
| 672 | stack_size: u32 = 0, | | |
| 673 | /// The stack alignment, which is 16 bytes by default. This is specified by the | | |
| 674 | /// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md | | |
| 675 | /// and also what the llvm backend will emit. | | |
| 676 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. | | |
| 677 | stack_alignment: Alignment = .@"16", | | |
| 678 | | | |
| 679 | // For each individual Wasm valtype we store a seperate free list which | | |
| 680 | // allows us to re-use locals that are no longer used. e.g. a temporary local. | | |
| 681 | /// A list of indexes which represents a local of valtype `i32`. | | |
| 682 | /// It is illegal to store a non-i32 valtype in this list. | | |
| 683 | free_locals_i32: std.ArrayListUnmanaged(u32) = .empty, | | |
| 684 | /// A list of indexes which represents a local of valtype `i64`. | | |
| 685 | /// It is illegal to store a non-i64 valtype in this list. | | |
| 686 | free_locals_i64: std.ArrayListUnmanaged(u32) = .empty, | | |
| 687 | /// A list of indexes which represents a local of valtype `f32`. | | |
| 688 | /// It is illegal to store a non-f32 valtype in this list. | | |
| 689 | free_locals_f32: std.ArrayListUnmanaged(u32) = .empty, | | |
| 690 | /// A list of indexes which represents a local of valtype `f64`. | | |
| 691 | /// It is illegal to store a non-f64 valtype in this list. | | |
| 692 | free_locals_f64: std.ArrayListUnmanaged(u32) = .empty, | | |
| 693 | /// A list of indexes which represents a local of valtype `v127`. | | |
| 694 | /// It is illegal to store a non-v128 valtype in this list. | | |
| 695 | free_locals_v128: std.ArrayListUnmanaged(u32) = .empty, | | |
| 696 | | | |
| 697 | /// When in debug mode, this tracks if no `finishAir` was missed. | | |
| 698 | /// Forgetting to call `finishAir` will cause the result to not be | | |
| 699 | /// stored in our `values` map and therefore cause bugs. | | |
| 700 | air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init, | | |
| 701 | | | |
| 702 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 697 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 703 | | 698 | |
| 704 | const InnerError = error{ | 699 | const InnerError = error{ |
| ... | @@ -719,8 +714,6 @@ pub fn deinit(func: *CodeGen) void { | ... | @@ -719,8 +714,6 @@ pub fn deinit(func: *CodeGen) void { |
| 719 | func.loops.deinit(func.gpa); | 714 | func.loops.deinit(func.gpa); |
| 720 | func.locals.deinit(func.gpa); | 715 | func.locals.deinit(func.gpa); |
| 721 | func.simd_immediates.deinit(func.gpa); | 716 | func.simd_immediates.deinit(func.gpa); |
| 722 | func.mir_instructions.deinit(func.gpa); | | |
| 723 | func.mir_extra.deinit(func.gpa); | | |
| 724 | func.free_locals_i32.deinit(func.gpa); | 717 | func.free_locals_i32.deinit(func.gpa); |
| 725 | func.free_locals_i64.deinit(func.gpa); | 718 | func.free_locals_i64.deinit(func.gpa); |
| 726 | func.free_locals_f32.deinit(func.gpa); | 719 | func.free_locals_f32.deinit(func.gpa); |
| ... | @@ -729,9 +722,10 @@ pub fn deinit(func: *CodeGen) void { | ... | @@ -729,9 +722,10 @@ pub fn deinit(func: *CodeGen) void { |
| 729 | func.* = undefined; | 722 | func.* = undefined; |
| 730 | } | 723 | } |
| 731 | | 724 | |
| 732 | fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | 725 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 733 | const msg = try Zcu.ErrorMsg.create(func.gpa, func.src_loc, fmt, args); | 726 | const zcu = cg.pt.zcu; |
| 734 | return func.pt.zcu.codegenFailMsg(func.owner_nav, msg); | 727 | const func = zcu.funcInfo(cg.func_index); |
| | 728 | return zcu.codegenFail(func.owner_nav, fmt, args); |
| 735 | } | 729 | } |
| 736 | | 730 | |
| 737 | /// Resolves the `WValue` for the given instruction `inst` | 731 | /// Resolves the `WValue` for the given instruction `inst` |
| ... | @@ -767,7 +761,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | ... | @@ -767,7 +761,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 767 | // | 761 | // |
| 768 | // In the other cases, we will simply lower the constant to a value that fits | 762 | // In the other cases, we will simply lower the constant to a value that fits |
| 769 | // into a single local (such as a pointer, integer, bool, etc). | 763 | // into a single local (such as a pointer, integer, bool, etc). |
| 770 | const result: WValue = if (isByRef(ty, pt, func.target.*)) | 764 | const result: WValue = if (isByRef(ty, pt, func.target)) |
| 771 | .{ .memory = val.toIntern() } | 765 | .{ .memory = val.toIntern() } |
| 772 | else | 766 | else |
| 773 | try func.lowerConstant(val, ty); | 767 | try func.lowerConstant(val, ty); |
| ... | @@ -885,8 +879,12 @@ fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!vo | ... | @@ -885,8 +879,12 @@ fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!vo |
| 885 | try func.addInst(.{ .tag = tag, .data = .{ .label = label } }); | 879 | try func.addInst(.{ .tag = tag, .data = .{ .label = label } }); |
| 886 | } | 880 | } |
| 887 | | 881 | |
| 888 | fn addCallTagName(func: *CodeGen, ip_index: InternPool.Index) error{OutOfMemory}!void { | 882 | fn addIpIndex(func: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Index) Allocator.Error!void { |
| 889 | try func.addInst(.{ .tag = .call_tag_name, .data = .{ .ip_index = ip_index } }); | 883 | try func.addInst(.{ .tag = tag, .data = .{ .ip_index = i } }); |
| | 884 | } |
| | 885 | |
| | 886 | fn addNav(func: *CodeGen, tag: Mir.Inst.Tag, i: InternPool.Nav.Index) Allocator.Error!void { |
| | 887 | try func.addInst(.{ .tag = tag, .data = .{ .nav_index = i } }); |
| 890 | } | 888 | } |
| 891 | | 889 | |
| 892 | /// Accepts an unsigned 32bit integer rather than a signed integer to | 890 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| ... | @@ -900,7 +898,7 @@ fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void { | ... | @@ -900,7 +898,7 @@ fn addImm32(func: *CodeGen, imm: u32) error{OutOfMemory}!void { |
| 900 | /// prevent us from having to bitcast multiple times as most values | 898 | /// prevent us from having to bitcast multiple times as most values |
| 901 | /// within codegen are represented as unsigned rather than signed. | 899 | /// within codegen are represented as unsigned rather than signed. |
| 902 | fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { | 900 | fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { |
| 903 | const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm)); | 901 | const extra_index = try func.addExtra(Mir.Imm64.init(imm)); |
| 904 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); | 902 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); |
| 905 | } | 903 | } |
| 906 | | 904 | |
| ... | @@ -916,7 +914,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void { | ... | @@ -916,7 +914,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void { |
| 916 | } | 914 | } |
| 917 | | 915 | |
| 918 | fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void { | 916 | fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void { |
| 919 | const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float)); | 917 | const extra_index = try func.addExtra(Mir.Float64.init(float)); |
| 920 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | 918 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); |
| 921 | } | 919 | } |
| 922 | | 920 | |
| ... | @@ -956,6 +954,8 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 | ... | @@ -956,6 +954,8 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 |
| 956 | inline for (fields) |field| { | 954 | inline for (fields) |field| { |
| 957 | func.mir_extra.appendAssumeCapacity(switch (field.type) { | 955 | func.mir_extra.appendAssumeCapacity(switch (field.type) { |
| 958 | u32 => @field(extra, field.name), | 956 | u32 => @field(extra, field.name), |
| | 957 | i32 => @bitCast(@field(extra, field.name)), |
| | 958 | InternPool.Index => @intFromEnum(@field(extra, field.name)), |
| 959 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), | 959 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 960 | }); | 960 | }); |
| 961 | } | 961 | } |
| ... | @@ -963,11 +963,11 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 | ... | @@ -963,11 +963,11 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 |
| 963 | } | 963 | } |
| 964 | | 964 | |
| 965 | /// Using a given `Type`, returns the corresponding valtype for .auto callconv | 965 | /// Using a given `Type`, returns the corresponding valtype for .auto callconv |
| 966 | fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valtype { | 966 | fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: *const std.Target) std.wasm.Valtype { |
| 967 | const zcu = pt.zcu; | 967 | const zcu = pt.zcu; |
| 968 | const ip = &zcu.intern_pool; | 968 | const ip = &zcu.intern_pool; |
| 969 | return switch (ty.zigTypeTag(zcu)) { | 969 | return switch (ty.zigTypeTag(zcu)) { |
| 970 | .float => switch (ty.floatBits(target)) { | 970 | .float => switch (ty.floatBits(target.*)) { |
| 971 | 16 => .i32, // stored/loaded as u16 | 971 | 16 => .i32, // stored/loaded as u16 |
| 972 | 32 => .f32, | 972 | 32 => .f32, |
| 973 | 64 => .f64, | 973 | 64 => .f64, |
| ... | @@ -1003,14 +1003,14 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valty | ... | @@ -1003,14 +1003,14 @@ fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valty |
| 1003 | } | 1003 | } |
| 1004 | | 1004 | |
| 1005 | /// Using a given `Type`, returns the byte representation of its wasm value type | 1005 | /// Using a given `Type`, returns the byte representation of its wasm value type |
| 1006 | fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 { | 1006 | fn genValtype(ty: Type, pt: Zcu.PerThread, target: *const std.Target) u8 { |
| 1007 | return @intFromEnum(typeToValtype(ty, pt, target)); | 1007 | return @intFromEnum(typeToValtype(ty, pt, target)); |
| 1008 | } | 1008 | } |
| 1009 | | 1009 | |
| 1010 | /// Using a given `Type`, returns the corresponding wasm value type | 1010 | /// Using a given `Type`, returns the corresponding wasm value type |
| 1011 | /// Differently from `genValtype` this also allows `void` to create a block | 1011 | /// Differently from `genValtype` this also allows `void` to create a block |
| 1012 | /// with no return type | 1012 | /// with no return type |
| 1013 | fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 { | 1013 | fn genBlockType(ty: Type, pt: Zcu.PerThread, target: *const std.Target) u8 { |
| 1014 | return switch (ty.ip_index) { | 1014 | return switch (ty.ip_index) { |
| 1015 | .void_type, .noreturn_type => std.wasm.block_empty, | 1015 | .void_type, .noreturn_type => std.wasm.block_empty, |
| 1016 | else => genValtype(ty, pt, target), | 1016 | else => genValtype(ty, pt, target), |
| ... | @@ -1028,15 +1028,17 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { | ... | @@ -1028,15 +1028,17 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { |
| 1028 | .imm128 => |val| try func.addImm128(val), | 1028 | .imm128 => |val| try func.addImm128(val), |
| 1029 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | 1029 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 1030 | .float64 => |val| try func.addFloat64(val), | 1030 | .float64 => |val| try func.addFloat64(val), |
| 1031 | .memory => |ptr| { | 1031 | .memory => |ptr| try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = ptr } }), |
| 1032 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 }); | 1032 | .memory_offset => |mo| try func.addInst(.{ |
| 1033 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | 1033 | .tag = .uav_ref_off, |
| 1034 | }, | 1034 | .data = .{ |
| 1035 | .memory_offset => |mem_off| { | 1035 | .payload = try func.addExtra(Mir.UavRefOff{ |
| 1036 | const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset }); | 1036 | .ip_index = mo.pointer, |
| 1037 | try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); | 1037 | .offset = @intCast(mo.offset), // TODO should not be an assert |
| 1038 | }, | 1038 | }), |
| 1039 | .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation | 1039 | }, |
| | 1040 | }), |
| | 1041 | .function_index => |index| try func.addIpIndex(.function_index, index), |
| 1040 | .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset | 1042 | .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset |
| 1041 | } | 1043 | } |
| 1042 | } | 1044 | } |
| ... | @@ -1075,7 +1077,7 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { | ... | @@ -1075,7 +1077,7 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 1075 | /// Returns a corresponding `Wvalue` with `local` as active tag | 1077 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 1076 | fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | 1078 | fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { |
| 1077 | const pt = func.pt; | 1079 | const pt = func.pt; |
| 1078 | const valtype = typeToValtype(ty, pt, func.target.*); | 1080 | const valtype = typeToValtype(ty, pt, func.target); |
| 1079 | const index_or_null = switch (valtype) { | 1081 | const index_or_null = switch (valtype) { |
| 1080 | .i32 => func.free_locals_i32.popOrNull(), | 1082 | .i32 => func.free_locals_i32.popOrNull(), |
| 1081 | .i64 => func.free_locals_i64.popOrNull(), | 1083 | .i64 => func.free_locals_i64.popOrNull(), |
| ... | @@ -1095,7 +1097,7 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -1095,7 +1097,7 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { |
| 1095 | /// to use a zero-initialized local. | 1097 | /// to use a zero-initialized local. |
| 1096 | fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | 1098 | fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue { |
| 1097 | const pt = func.pt; | 1099 | const pt = func.pt; |
| 1098 | try func.locals.append(func.gpa, genValtype(ty, pt, func.target.*)); | 1100 | try func.locals.append(func.gpa, genValtype(ty, pt, func.target)); |
| 1099 | const initial_index = func.local_index; | 1101 | const initial_index = func.local_index; |
| 1100 | func.local_index += 1; | 1102 | func.local_index += 1; |
| 1101 | return .{ .local = .{ .value = initial_index, .references = 1 } }; | 1103 | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| ... | @@ -1107,7 +1109,7 @@ fn genFunctype( | ... | @@ -1107,7 +1109,7 @@ fn genFunctype( |
| 1107 | params: []const InternPool.Index, | 1109 | params: []const InternPool.Index, |
| 1108 | return_type: Type, | 1110 | return_type: Type, |
| 1109 | pt: Zcu.PerThread, | 1111 | pt: Zcu.PerThread, |
| 1110 | target: std.Target, | 1112 | target: *const std.Target, |
| 1111 | ) !link.File.Wasm.FunctionType.Index { | 1113 | ) !link.File.Wasm.FunctionType.Index { |
| 1112 | const zcu = pt.zcu; | 1114 | const zcu = pt.zcu; |
| 1113 | const gpa = zcu.gpa; | 1115 | const gpa = zcu.gpa; |
| ... | @@ -1162,150 +1164,206 @@ fn genFunctype( | ... | @@ -1162,150 +1164,206 @@ fn genFunctype( |
| 1162 | }); | 1164 | }); |
| 1163 | } | 1165 | } |
| 1164 | | 1166 | |
| 1165 | pub fn generate( | 1167 | pub const Function = extern struct { |
| 1166 | bin_file: *link.File, | 1168 | /// Index into `Wasm.mir_instructions`. |
| | 1169 | mir_off: u32, |
| | 1170 | /// This is unused except for as a safety slice bound and could be removed. |
| | 1171 | mir_len: u32, |
| | 1172 | /// Index into `Wasm.mir_extra`. |
| | 1173 | mir_extra_off: u32, |
| | 1174 | /// This is unused except for as a safety slice bound and could be removed. |
| | 1175 | mir_extra_len: u32, |
| | 1176 | locals_off: u32, |
| | 1177 | locals_len: u32, |
| | 1178 | prologue: Prologue, |
| | 1179 | |
| | 1180 | pub const Prologue = extern struct { |
| | 1181 | flags: Flags, |
| | 1182 | sp_local: u32, |
| | 1183 | stack_size: u32, |
| | 1184 | bottom_stack_local: u32, |
| | 1185 | |
| | 1186 | pub const Flags = packed struct(u32) { |
| | 1187 | stack_alignment: Alignment, |
| | 1188 | padding: u26 = 0, |
| | 1189 | }; |
| | 1190 | |
| | 1191 | pub const none: Prologue = .{ |
| | 1192 | .sp_local = 0, |
| | 1193 | .flags = .{ .stack_alignment = .none }, |
| | 1194 | .stack_size = 0, |
| | 1195 | .bottom_stack_local = 0, |
| | 1196 | }; |
| | 1197 | |
| | 1198 | pub fn isNone(p: *const Prologue) bool { |
| | 1199 | return p.flags.stack_alignment != .none; |
| | 1200 | } |
| | 1201 | }; |
| | 1202 | |
| | 1203 | pub fn lower(f: *Function, wasm: *const Wasm, code: *std.ArrayList(u8)) Allocator.Error!void { |
| | 1204 | const gpa = wasm.base.comp.gpa; |
| | 1205 | |
| | 1206 | // Write the locals in the prologue of the function body. |
| | 1207 | const locals = wasm.all_zcu_locals[f.locals_off..][0..f.locals_len]; |
| | 1208 | try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38); |
| | 1209 | |
| | 1210 | std.leb.writeUleb128(code.writer(gpa), @as(u32, @intCast(locals.len))) catch unreachable; |
| | 1211 | for (locals) |local| { |
| | 1212 | std.leb.writeUleb128(code.writer(gpa), @as(u32, 1)) catch unreachable; |
| | 1213 | code.appendAssumeCapacity(local); |
| | 1214 | } |
| | 1215 | |
| | 1216 | // Stack management section of function prologue. |
| | 1217 | const stack_alignment = f.prologue.flags.stack_alignment; |
| | 1218 | if (stack_alignment.toByteUnits()) |align_bytes| { |
| | 1219 | const sp_global = try wasm.stackPointerGlobalIndex(); |
| | 1220 | // load stack pointer |
| | 1221 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); |
| | 1222 | std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable; |
| | 1223 | // store stack pointer so we can restore it when we return from the function |
| | 1224 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); |
| | 1225 | leb.writeUleb128(code.writer(gpa), f.prologue.sp_local) catch unreachable; |
| | 1226 | // get the total stack size |
| | 1227 | const aligned_stack: i32 = @intCast(f.stack_alignment.forward(f.prologue.stack_size)); |
| | 1228 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| | 1229 | leb.writeIleb128(code.writer(gpa), aligned_stack) catch unreachable; |
| | 1230 | // subtract it from the current stack pointer |
| | 1231 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); |
| | 1232 | // Get negative stack alignment |
| | 1233 | const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; |
| | 1234 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| | 1235 | leb.writeIleb128(code.writer(gpa), neg_stack_align) catch unreachable; |
| | 1236 | // Bitwise-and the value to get the new stack pointer to ensure the |
| | 1237 | // pointers are aligned with the abi alignment. |
| | 1238 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); |
| | 1239 | // The bottom will be used to calculate all stack pointer offsets. |
| | 1240 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); |
| | 1241 | leb.writeUleb128(code.writer(gpa), f.prologue.bottom_stack_local) catch unreachable; |
| | 1242 | // Store the current stack pointer value into the global stack pointer so other function calls will |
| | 1243 | // start from this value instead and not overwrite the current stack. |
| | 1244 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); |
| | 1245 | std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable; |
| | 1246 | } |
| | 1247 | |
| | 1248 | var emit: Emit = .{ |
| | 1249 | .mir = .{ |
| | 1250 | .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len], |
| | 1251 | .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len], |
| | 1252 | .extra = wasm.mir_extra[f.mir_extra_off..][0..f.mir_extra_len], |
| | 1253 | }, |
| | 1254 | .wasm = wasm, |
| | 1255 | .code = code, |
| | 1256 | }; |
| | 1257 | try emit.lowerToCode(); |
| | 1258 | } |
| | 1259 | }; |
| | 1260 | |
| | 1261 | pub const Error = error{ |
| | 1262 | OutOfMemory, |
| | 1263 | /// Compiler was asked to operate on a number larger than supported. |
| | 1264 | Overflow, |
| | 1265 | /// Indicates the error is already stored in Zcu `failed_codegen`. |
| | 1266 | CodegenFail, |
| | 1267 | }; |
| | 1268 | |
| | 1269 | pub fn function( |
| | 1270 | wasm: *Wasm, |
| 1167 | pt: Zcu.PerThread, | 1271 | pt: Zcu.PerThread, |
| 1168 | src_loc: Zcu.LazySrcLoc, | | |
| 1169 | func_index: InternPool.Index, | 1272 | func_index: InternPool.Index, |
| 1170 | air: Air, | 1273 | air: Air, |
| 1171 | liveness: Liveness, | 1274 | liveness: Liveness, |
| 1172 | code: *std.ArrayListUnmanaged(u8), | 1275 | ) Error!Function { |
| 1173 | debug_output: link.File.DebugInfoOutput, | | |
| 1174 | ) codegen.CodeGenError!void { | | |
| 1175 | const zcu = pt.zcu; | 1276 | const zcu = pt.zcu; |
| 1176 | const gpa = zcu.gpa; | 1277 | const gpa = zcu.gpa; |
| 1177 | const func = zcu.funcInfo(func_index); | 1278 | const func = zcu.funcInfo(func_index); |
| 1178 | const file_scope = zcu.navFileScope(func.owner_nav); | 1279 | const file_scope = zcu.navFileScope(func.owner_nav); |
| 1179 | const target = &file_scope.mod.resolved_target.result; | 1280 | const target = &file_scope.mod.resolved_target.result; |
| | 1281 | const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu); |
| | 1282 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| | 1283 | const ip = &zcu.intern_pool; |
| | 1284 | const fn_ty_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, target); |
| | 1285 | const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); |
| | 1286 | const any_returns = returns.len != 0; |
| | 1287 | |
| | 1288 | var cc_result = try resolveCallingConventionValues(pt, fn_ty, target); |
| | 1289 | defer cc_result.deinit(gpa); |
| | 1290 | |
| 1180 | var code_gen: CodeGen = .{ | 1291 | var code_gen: CodeGen = .{ |
| 1181 | .gpa = gpa, | 1292 | .gpa = gpa, |
| 1182 | .pt = pt, | 1293 | .pt = pt, |
| 1183 | .air = air, | 1294 | .air = air, |
| 1184 | .liveness = liveness, | 1295 | .liveness = liveness, |
| 1185 | .code = code, | | |
| 1186 | .owner_nav = func.owner_nav, | 1296 | .owner_nav = func.owner_nav, |
| 1187 | .src_loc = src_loc, | | |
| 1188 | .locals = .{}, | | |
| 1189 | .target = target, | 1297 | .target = target, |
| 1190 | .bin_file = bin_file.cast(.wasm).?, | 1298 | .wasm = wasm, |
| 1191 | .debug_output = debug_output, | | |
| 1192 | .func_index = func_index, | 1299 | .func_index = func_index, |
| | 1300 | .args = cc_result.args, |
| | 1301 | .return_value = cc_result.return_value, |
| | 1302 | .local_index = cc_result.local_index, |
| | 1303 | .mir_instructions = &wasm.mir_instructions, |
| | 1304 | .mir_extra = &wasm.mir_extra, |
| | 1305 | .locals = &wasm.all_zcu_locals, |
| 1193 | }; | 1306 | }; |
| 1194 | defer code_gen.deinit(); | 1307 | defer code_gen.deinit(); |
| 1195 | | 1308 | |
| 1196 | genFunc(&code_gen) catch |err| switch (err) { | 1309 | return functionInner(&code_gen, any_returns) catch |err| switch (err) { |
| 1197 | error.CodegenFail => return error.CodegenFail, | 1310 | error.CodegenFail => return error.CodegenFail, |
| 1198 | else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), | 1311 | else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), |
| 1199 | }; | 1312 | }; |
| 1200 | } | 1313 | } |
| 1201 | | 1314 | |
| 1202 | fn genFunc(func: *CodeGen) InnerError!void { | 1315 | fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { |
| 1203 | const wasm = func.bin_file; | 1316 | const wasm = cg.wasm; |
| 1204 | const pt = func.pt; | 1317 | const pt = cg.pt; |
| 1205 | const zcu = pt.zcu; | 1318 | const zcu = pt.zcu; |
| 1206 | const ip = &zcu.intern_pool; | | |
| 1207 | const fn_ty = zcu.navValue(func.owner_nav).typeOf(zcu); | | |
| 1208 | const fn_info = zcu.typeToFunc(fn_ty).?; | | |
| 1209 | const fn_ty_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*); | | |
| 1210 | | 1319 | |
| 1211 | var cc_result = try func.resolveCallingConventionValues(fn_ty); | 1320 | const start_mir_off: u32 = @intCast(wasm.mir_instructions.len); |
| 1212 | defer cc_result.deinit(func.gpa); | 1321 | const start_mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len); |
| | 1322 | const start_locals_off: u32 = @intCast(wasm.all_zcu_locals.items.len); |
| 1213 | | 1323 | |
| 1214 | func.args = cc_result.args; | 1324 | try cg.branches.append(cg.gpa, .{}); |
| 1215 | func.return_value = cc_result.return_value; | | |
| 1216 | | | |
| 1217 | try func.addTag(.dbg_prologue_end); | | |
| 1218 | | | |
| 1219 | try func.branches.append(func.gpa, .{}); | | |
| 1220 | // clean up outer branch | 1325 | // clean up outer branch |
| 1221 | defer { | 1326 | defer { |
| 1222 | var outer_branch = func.branches.pop(); | 1327 | var outer_branch = cg.branches.pop(); |
| 1223 | outer_branch.deinit(func.gpa); | 1328 | outer_branch.deinit(cg.gpa); |
| 1224 | assert(func.branches.items.len == 0); // missing branch merge | 1329 | assert(cg.branches.items.len == 0); // missing branch merge |
| 1225 | } | 1330 | } |
| 1226 | // Generate MIR for function body | 1331 | // Generate MIR for function body |
| 1227 | try func.genBody(func.air.getMainBody()); | 1332 | try cg.genBody(cg.air.getMainBody()); |
| 1228 | | 1333 | |
| 1229 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) | 1334 | // In case we have a return value, but the last instruction is a noreturn (such as a while loop) |
| 1230 | // we emit an unreachable instruction to tell the stack validator that part will never be reached. | 1335 | // we emit an unreachable instruction to tell the stack validator that part will never be reached. |
| 1231 | const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); | 1336 | if (any_returns and cg.air.instructions.len > 0) { |
| 1232 | if (returns.len != 0 and func.air.instructions.len > 0) { | 1337 | const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1); |
| 1233 | const inst: Air.Inst.Index = @enumFromInt(func.air.instructions.len - 1); | 1338 | const last_inst_ty = cg.typeOfIndex(inst); |
| 1234 | const last_inst_ty = func.typeOfIndex(inst); | | |
| 1235 | if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) { | 1339 | if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) { |
| 1236 | try func.addTag(.@"unreachable"); | 1340 | try cg.addTag(.@"unreachable"); |
| 1237 | } | 1341 | } |
| 1238 | } | 1342 | } |
| 1239 | // End of function body | 1343 | // End of function body |
| 1240 | try func.addTag(.end); | 1344 | try cg.addTag(.end); |
| 1241 | | 1345 | try cg.addTag(.dbg_epilogue_begin); |
| 1242 | try func.addTag(.dbg_epilogue_begin); | 1346 | |
| 1243 | | 1347 | return .{ |
| 1244 | // check if we have to initialize and allocate anything into the stack frame. | 1348 | .mir_off = start_mir_off, |
| 1245 | // If so, create enough stack space and insert the instructions at the front of the list. | 1349 | .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), |
| 1246 | if (func.initial_stack_value != .none) { | 1350 | .mir_extra_off = start_mir_extra_off, |
| 1247 | var prologue = std.ArrayList(Mir.Inst).init(func.gpa); | 1351 | .mir_extra_len = @intCast(wasm.mir_extra.items.len - start_mir_extra_off), |
| 1248 | defer prologue.deinit(); | 1352 | .locals_off = start_locals_off, |
| 1249 | | 1353 | .locals_len = @intCast(wasm.all_zcu_locals.items.len - start_locals_off), |
| 1250 | const sp = @intFromEnum(wasm.zig_object.?.stack_pointer_sym); | 1354 | .prologue = if (cg.initial_stack_value == .none) .none else .{ |
| 1251 | // load stack pointer | 1355 | .sp_local = cg.initial_stack_value.local.value, |
| 1252 | try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } }); | 1356 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 1253 | // store stack pointer so we can restore it when we return from the function | 1357 | .stack_size = cg.stack_size, |
| 1254 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } }); | 1358 | .bottom_stack_local = cg.bottom_stack_value.local.value, |
| 1255 | // get the total stack size | | |
| 1256 | const aligned_stack = func.stack_alignment.forward(func.stack_size); | | |
| 1257 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(aligned_stack) } }); | | |
| 1258 | // subtract it from the current stack pointer | | |
| 1259 | try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } }); | | |
| 1260 | // Get negative stack alignment | | |
| 1261 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @as(i32, @intCast(func.stack_alignment.toByteUnits().?)) * -1 } }); | | |
| 1262 | // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment | | |
| 1263 | try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } }); | | |
| 1264 | // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets | | |
| 1265 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } }); | | |
| 1266 | // Store the current stack pointer value into the global stack pointer so other function calls will | | |
| 1267 | // start from this value instead and not overwrite the current stack. | | |
| 1268 | try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } }); | | |
| 1269 | | | |
| 1270 | // reserve space and insert all prologue instructions at the front of the instruction list | | |
| 1271 | // We insert them in reserve order as there is no insertSlice in multiArrayList. | | |
| 1272 | try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len); | | |
| 1273 | for (prologue.items, 0..) |_, index| { | | |
| 1274 | const inst = prologue.items[prologue.items.len - 1 - index]; | | |
| 1275 | func.mir_instructions.insertAssumeCapacity(0, inst); | | |
| 1276 | } | | |
| 1277 | } | | |
| 1278 | | | |
| 1279 | var mir: Mir = .{ | | |
| 1280 | .instructions = func.mir_instructions.toOwnedSlice(), | | |
| 1281 | .extra = try func.mir_extra.toOwnedSlice(func.gpa), | | |
| 1282 | }; | | |
| 1283 | defer mir.deinit(func.gpa); | | |
| 1284 | | | |
| 1285 | var emit: Emit = .{ | | |
| 1286 | .mir = mir, | | |
| 1287 | .bin_file = wasm, | | |
| 1288 | .code = func.code, | | |
| 1289 | .locals = func.locals.items, | | |
| 1290 | .owner_nav = func.owner_nav, | | |
| 1291 | .dbg_output = func.debug_output, | | |
| 1292 | .prev_di_line = 0, | | |
| 1293 | .prev_di_column = 0, | | |
| 1294 | .prev_di_offset = 0, | | |
| 1295 | }; | | |
| 1296 | | | |
| 1297 | emit.emitMir() catch |err| switch (err) { | | |
| 1298 | error.EmitFail => { | | |
| 1299 | func.err_msg = emit.error_msg.?; | | |
| 1300 | return error.CodegenFail; | | |
| 1301 | }, | 1359 | }, |
| 1302 | else => |e| return e, | | |
| 1303 | }; | 1360 | }; |
| 1304 | } | 1361 | } |
| 1305 | | 1362 | |
| 1306 | const CallWValues = struct { | 1363 | const CallWValues = struct { |
| 1307 | args: []WValue, | 1364 | args: []WValue, |
| 1308 | return_value: WValue, | 1365 | return_value: WValue, |
| | 1366 | local_index: u32, |
| 1309 | | 1367 | |
| 1310 | fn deinit(values: *CallWValues, gpa: Allocator) void { | 1368 | fn deinit(values: *CallWValues, gpa: Allocator) void { |
| 1311 | gpa.free(values.args); | 1369 | gpa.free(values.args); |
| ... | @@ -1313,28 +1371,34 @@ const CallWValues = struct { | ... | @@ -1313,28 +1371,34 @@ const CallWValues = struct { |
| 1313 | } | 1371 | } |
| 1314 | }; | 1372 | }; |
| 1315 | | 1373 | |
| 1316 | fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues { | 1374 | fn resolveCallingConventionValues( |
| 1317 | const pt = func.pt; | 1375 | pt: Zcu.PerThread, |
| | 1376 | fn_ty: Type, |
| | 1377 | target: *const std.Target, |
| | 1378 | ) Allocator.Error!CallWValues { |
| 1318 | const zcu = pt.zcu; | 1379 | const zcu = pt.zcu; |
| | 1380 | const gpa = zcu.gpa; |
| 1319 | const ip = &zcu.intern_pool; | 1381 | const ip = &zcu.intern_pool; |
| 1320 | const fn_info = zcu.typeToFunc(fn_ty).?; | 1382 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1321 | const cc = fn_info.cc; | 1383 | const cc = fn_info.cc; |
| | 1384 | |
| 1322 | var result: CallWValues = .{ | 1385 | var result: CallWValues = .{ |
| 1323 | .args = &.{}, | 1386 | .args = &.{}, |
| 1324 | .return_value = .none, | 1387 | .return_value = .none, |
| | 1388 | .local_index = 0, |
| 1325 | }; | 1389 | }; |
| 1326 | if (cc == .naked) return result; | 1390 | if (cc == .naked) return result; |
| 1327 | | 1391 | |
| 1328 | var args = std.ArrayList(WValue).init(func.gpa); | 1392 | var args = std.ArrayList(WValue).init(gpa); |
| 1329 | defer args.deinit(); | 1393 | defer args.deinit(); |
| 1330 | | 1394 | |
| 1331 | // Check if we store the result as a pointer to the stack rather than | 1395 | // Check if we store the result as a pointer to the stack rather than |
| 1332 | // by value | 1396 | // by value |
| 1333 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 1397 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, target)) { |
| 1334 | // the sret arg will be passed as first argument, therefore we | 1398 | // the sret arg will be passed as first argument, therefore we |
| 1335 | // set the `return_value` before allocating locals for regular args. | 1399 | // set the `return_value` before allocating locals for regular args. |
| 1336 | result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } }; | 1400 | result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } }; |
| 1337 | func.local_index += 1; | 1401 | result.local_index += 1; |
| 1338 | } | 1402 | } |
| 1339 | | 1403 | |
| 1340 | switch (cc) { | 1404 | switch (cc) { |
| ... | @@ -1344,8 +1408,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV | ... | @@ -1344,8 +1408,8 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1344 | continue; | 1408 | continue; |
| 1345 | } | 1409 | } |
| 1346 | | 1410 | |
| 1347 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | 1411 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1348 | func.local_index += 1; | 1412 | result.local_index += 1; |
| 1349 | } | 1413 | } |
| 1350 | }, | 1414 | }, |
| 1351 | .wasm_watc => { | 1415 | .wasm_watc => { |
| ... | @@ -1353,18 +1417,23 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV | ... | @@ -1353,18 +1417,23 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV |
| 1353 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); | 1417 | const ty_classes = abi.classifyType(Type.fromInterned(ty), zcu); |
| 1354 | for (ty_classes) |class| { | 1418 | for (ty_classes) |class| { |
| 1355 | if (class == .none) continue; | 1419 | if (class == .none) continue; |
| 1356 | try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } }); | 1420 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1357 | func.local_index += 1; | 1421 | result.local_index += 1; |
| 1358 | } | 1422 | } |
| 1359 | } | 1423 | } |
| 1360 | }, | 1424 | }, |
| 1361 | else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}), | 1425 | else => unreachable, // Frontend is responsible for emitting an error earlier. |
| 1362 | } | 1426 | } |
| 1363 | result.args = try args.toOwnedSlice(); | 1427 | result.args = try args.toOwnedSlice(); |
| 1364 | return result; | 1428 | return result; |
| 1365 | } | 1429 | } |
| 1366 | | 1430 | |
| 1367 | fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool { | 1431 | fn firstParamSRet( |
| | 1432 | cc: std.builtin.CallingConvention, |
| | 1433 | return_type: Type, |
| | 1434 | pt: Zcu.PerThread, |
| | 1435 | target: *const std.Target, |
| | 1436 | ) bool { |
| 1368 | switch (cc) { | 1437 | switch (cc) { |
| 1369 | .@"inline" => unreachable, | 1438 | .@"inline" => unreachable, |
| 1370 | .auto => return isByRef(return_type, pt, target), | 1439 | .auto => return isByRef(return_type, pt, target), |
| ... | @@ -1466,8 +1535,7 @@ fn restoreStackPointer(func: *CodeGen) !void { | ... | @@ -1466,8 +1535,7 @@ fn restoreStackPointer(func: *CodeGen) !void { |
| 1466 | // Get the original stack pointer's value | 1535 | // Get the original stack pointer's value |
| 1467 | try func.emitWValue(func.initial_stack_value); | 1536 | try func.emitWValue(func.initial_stack_value); |
| 1468 | | 1537 | |
| 1469 | // save its value in the global stack pointer | 1538 | try func.addTag(.global_set_sp); |
| 1470 | try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym)); | | |
| 1471 | } | 1539 | } |
| 1472 | | 1540 | |
| 1473 | /// From a given type, will create space on the virtual stack to store the value of such type. | 1541 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| ... | @@ -1675,7 +1743,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch { | ... | @@ -1675,7 +1743,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch { |
| 1675 | | 1743 | |
| 1676 | /// For a given `Type`, will return true when the type will be passed | 1744 | /// For a given `Type`, will return true when the type will be passed |
| 1677 | /// by reference, rather than by value | 1745 | /// by reference, rather than by value |
| 1678 | fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { | 1746 | fn isByRef(ty: Type, pt: Zcu.PerThread, target: *const std.Target) bool { |
| 1679 | const zcu = pt.zcu; | 1747 | const zcu = pt.zcu; |
| 1680 | const ip = &zcu.intern_pool; | 1748 | const ip = &zcu.intern_pool; |
| 1681 | switch (ty.zigTypeTag(zcu)) { | 1749 | switch (ty.zigTypeTag(zcu)) { |
| ... | @@ -1716,7 +1784,7 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { | ... | @@ -1716,7 +1784,7 @@ fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool { |
| 1716 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, | 1784 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, |
| 1717 | .int => return ty.intInfo(zcu).bits > 64, | 1785 | .int => return ty.intInfo(zcu).bits > 64, |
| 1718 | .@"enum" => return ty.intInfo(zcu).bits > 64, | 1786 | .@"enum" => return ty.intInfo(zcu).bits > 64, |
| 1719 | .float => return ty.floatBits(target) > 64, | 1787 | .float => return ty.floatBits(target.*) > 64, |
| 1720 | .error_union => { | 1788 | .error_union => { |
| 1721 | const pl_ty = ty.errorUnionPayload(zcu); | 1789 | const pl_ty = ty.errorUnionPayload(zcu); |
| 1722 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 1790 | if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| ... | @@ -1747,7 +1815,7 @@ const SimdStoreStrategy = enum { | ... | @@ -1747,7 +1815,7 @@ const SimdStoreStrategy = enum { |
| 1747 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd | 1815 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd |
| 1748 | /// features are enabled, the function will return `.direct`. This would allow to store | 1816 | /// features are enabled, the function will return `.direct`. This would allow to store |
| 1749 | /// it using a instruction, rather than an unrolled version. | 1817 | /// it using a instruction, rather than an unrolled version. |
| 1750 | fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy { | 1818 | fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: *const std.Target) SimdStoreStrategy { |
| 1751 | assert(ty.zigTypeTag(zcu) == .vector); | 1819 | assert(ty.zigTypeTag(zcu) == .vector); |
| 1752 | if (ty.bitSize(zcu) != 128) return .unrolled; | 1820 | if (ty.bitSize(zcu) != 128) return .unrolled; |
| 1753 | const hasFeature = std.Target.wasm.featureSetHas; | 1821 | const hasFeature = std.Target.wasm.featureSetHas; |
| ... | @@ -2076,7 +2144,7 @@ fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2076,7 +2144,7 @@ fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2076 | .op = .load, | 2144 | .op = .load, |
| 2077 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), | 2145 | .width = @as(u8, @intCast(scalar_type.abiSize(zcu) * 8)), |
| 2078 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, | 2146 | .signedness = if (scalar_type.isSignedInt(zcu)) .signed else .unsigned, |
| 2079 | .valtype1 = typeToValtype(scalar_type, pt, func.target.*), | 2147 | .valtype1 = typeToValtype(scalar_type, pt, func.target), |
| 2080 | }); | 2148 | }); |
| 2081 | try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ | 2149 | try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ |
| 2082 | .offset = operand.offset(), | 2150 | .offset = operand.offset(), |
| ... | @@ -2109,7 +2177,7 @@ fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2109,7 +2177,7 @@ fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2109 | } | 2177 | } |
| 2110 | | 2178 | |
| 2111 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; | 2179 | const fn_info = zcu.typeToFunc(zcu.navValue(func.owner_nav).typeOf(zcu)).?; |
| 2112 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 2180 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target)) { |
| 2113 | break :result func.return_value; | 2181 | break :result func.return_value; |
| 2114 | } | 2182 | } |
| 2115 | | 2183 | |
| ... | @@ -2131,7 +2199,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2131,7 +2199,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2131 | if (ret_ty.isError(zcu)) { | 2199 | if (ret_ty.isError(zcu)) { |
| 2132 | try func.addImm32(0); | 2200 | try func.addImm32(0); |
| 2133 | } | 2201 | } |
| 2134 | } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*)) { | 2202 | } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target)) { |
| 2135 | // leave on the stack | 2203 | // leave on the stack |
| 2136 | _ = try func.load(operand, ret_ty, 0); | 2204 | _ = try func.load(operand, ret_ty, 0); |
| 2137 | } | 2205 | } |
| ... | @@ -2142,7 +2210,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2142,7 +2210,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2142 | } | 2210 | } |
| 2143 | | 2211 | |
| 2144 | fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { | 2212 | fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { |
| 2145 | const wasm = func.bin_file; | 2213 | const wasm = func.wasm; |
| 2146 | if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); | 2214 | if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); |
| 2147 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 2215 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2148 | const extra = func.air.extraData(Air.Call, pl_op.payload); | 2216 | const extra = func.air.extraData(Air.Call, pl_op.payload); |
| ... | @@ -2159,7 +2227,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif | ... | @@ -2159,7 +2227,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2159 | }; | 2227 | }; |
| 2160 | const ret_ty = fn_ty.fnReturnType(zcu); | 2228 | const ret_ty = fn_ty.fnReturnType(zcu); |
| 2161 | const fn_info = zcu.typeToFunc(fn_ty).?; | 2229 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 2162 | const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target.*); | 2230 | const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), pt, func.target); |
| 2163 | | 2231 | |
| 2164 | const callee: ?InternPool.Nav.Index = blk: { | 2232 | const callee: ?InternPool.Nav.Index = blk: { |
| 2165 | const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null; | 2233 | const func_val = (try func.air.value(pl_op.operand, pt)) orelse break :blk null; |
| ... | @@ -2199,7 +2267,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif | ... | @@ -2199,7 +2267,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2199 | const operand = try func.resolveInst(pl_op.operand); | 2267 | const operand = try func.resolveInst(pl_op.operand); |
| 2200 | try func.emitWValue(operand); | 2268 | try func.emitWValue(operand); |
| 2201 | | 2269 | |
| 2202 | const fn_type_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*); | 2270 | const fn_type_index = try genFunctype(wasm, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target); |
| 2203 | try func.addLabel(.call_indirect, @intFromEnum(fn_type_index)); | 2271 | try func.addLabel(.call_indirect, @intFromEnum(fn_type_index)); |
| 2204 | } | 2272 | } |
| 2205 | | 2273 | |
| ... | @@ -2260,7 +2328,7 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void | ... | @@ -2260,7 +2328,7 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 2260 | // load the value, and then shift+or the rhs into the result location. | 2328 | // load the value, and then shift+or the rhs into the result location. |
| 2261 | const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8); | 2329 | const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8); |
| 2262 | | 2330 | |
| 2263 | if (isByRef(int_elem_ty, pt, func.target.*)) { | 2331 | if (isByRef(int_elem_ty, pt, func.target)) { |
| 2264 | return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); | 2332 | return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{}); |
| 2265 | } | 2333 | } |
| 2266 | | 2334 | |
| ... | @@ -2326,11 +2394,11 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE | ... | @@ -2326,11 +2394,11 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE |
| 2326 | const len = @as(u32, @intCast(abi_size)); | 2394 | const len = @as(u32, @intCast(abi_size)); |
| 2327 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2395 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2328 | }, | 2396 | }, |
| 2329 | .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target.*)) { | 2397 | .@"struct", .array, .@"union" => if (isByRef(ty, pt, func.target)) { |
| 2330 | const len = @as(u32, @intCast(abi_size)); | 2398 | const len = @as(u32, @intCast(abi_size)); |
| 2331 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2399 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2332 | }, | 2400 | }, |
| 2333 | .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target.*)) { | 2401 | .vector => switch (determineSimdStoreStrategy(ty, zcu, func.target)) { |
| 2334 | .unrolled => { | 2402 | .unrolled => { |
| 2335 | const len: u32 = @intCast(abi_size); | 2403 | const len: u32 = @intCast(abi_size); |
| 2336 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2404 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| ... | @@ -2388,7 +2456,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE | ... | @@ -2388,7 +2456,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE |
| 2388 | // into lhs, so we calculate that and emit that instead | 2456 | // into lhs, so we calculate that and emit that instead |
| 2389 | try func.lowerToStack(rhs); | 2457 | try func.lowerToStack(rhs); |
| 2390 | | 2458 | |
| 2391 | const valtype = typeToValtype(ty, pt, func.target.*); | 2459 | const valtype = typeToValtype(ty, pt, func.target); |
| 2392 | const opcode = buildOpcode(.{ | 2460 | const opcode = buildOpcode(.{ |
| 2393 | .valtype1 = valtype, | 2461 | .valtype1 = valtype, |
| 2394 | .width = @as(u8, @intCast(abi_size * 8)), | 2462 | .width = @as(u8, @intCast(abi_size * 8)), |
| ... | @@ -2417,7 +2485,7 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2417,7 +2485,7 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2417 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{ty_op.operand}); | 2485 | if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return func.finishAir(inst, .none, &.{ty_op.operand}); |
| 2418 | | 2486 | |
| 2419 | const result = result: { | 2487 | const result = result: { |
| 2420 | if (isByRef(ty, pt, func.target.*)) { | 2488 | if (isByRef(ty, pt, func.target)) { |
| 2421 | const new_local = try func.allocStack(ty); | 2489 | const new_local = try func.allocStack(ty); |
| 2422 | try func.store(new_local, operand, ty, 0); | 2490 | try func.store(new_local, operand, ty, 0); |
| 2423 | break :result new_local; | 2491 | break :result new_local; |
| ... | @@ -2467,7 +2535,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu | ... | @@ -2467,7 +2535,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu |
| 2467 | | 2535 | |
| 2468 | const abi_size: u8 = @intCast(ty.abiSize(zcu)); | 2536 | const abi_size: u8 = @intCast(ty.abiSize(zcu)); |
| 2469 | const opcode = buildOpcode(.{ | 2537 | const opcode = buildOpcode(.{ |
| 2470 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 2538 | .valtype1 = typeToValtype(ty, pt, func.target), |
| 2471 | .width = abi_size * 8, | 2539 | .width = abi_size * 8, |
| 2472 | .op = .load, | 2540 | .op = .load, |
| 2473 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, | 2541 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| ... | @@ -2517,19 +2585,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2517,19 +2585,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2517 | func.arg_index += 1; | 2585 | func.arg_index += 1; |
| 2518 | } | 2586 | } |
| 2519 | | 2587 | |
| 2520 | switch (func.debug_output) { | | |
| 2521 | .dwarf => |dwarf| { | | |
| 2522 | const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; | | |
| 2523 | if (name != .none) try dwarf.genLocalDebugInfo( | | |
| 2524 | .local_arg, | | |
| 2525 | name.toSlice(func.air), | | |
| 2526 | arg_ty, | | |
| 2527 | .{ .wasm_ext = .{ .local = arg.local.value } }, | | |
| 2528 | ); | | |
| 2529 | }, | | |
| 2530 | else => {}, | | |
| 2531 | } | | |
| 2532 | | | |
| 2533 | return func.finishAir(inst, arg, &.{}); | 2588 | return func.finishAir(inst, arg, &.{}); |
| 2534 | } | 2589 | } |
| 2535 | | 2590 | |
| ... | @@ -2577,7 +2632,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! | ... | @@ -2577,7 +2632,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2577 | return func.floatOp(float_op, ty, &.{ lhs, rhs }); | 2632 | return func.floatOp(float_op, ty, &.{ lhs, rhs }); |
| 2578 | } | 2633 | } |
| 2579 | | 2634 | |
| 2580 | if (isByRef(ty, pt, func.target.*)) { | 2635 | if (isByRef(ty, pt, func.target)) { |
| 2581 | if (ty.zigTypeTag(zcu) == .int) { | 2636 | if (ty.zigTypeTag(zcu) == .int) { |
| 2582 | return func.binOpBigInt(lhs, rhs, ty, op); | 2637 | return func.binOpBigInt(lhs, rhs, ty, op); |
| 2583 | } else { | 2638 | } else { |
| ... | @@ -2590,7 +2645,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! | ... | @@ -2590,7 +2645,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! |
| 2590 | | 2645 | |
| 2591 | const opcode: std.wasm.Opcode = buildOpcode(.{ | 2646 | const opcode: std.wasm.Opcode = buildOpcode(.{ |
| 2592 | .op = op, | 2647 | .op = op, |
| 2593 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 2648 | .valtype1 = typeToValtype(ty, pt, func.target), |
| 2594 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, | 2649 | .signedness = if (ty.isSignedInt(zcu)) .signed else .unsigned, |
| 2595 | }); | 2650 | }); |
| 2596 | try func.emitWValue(lhs); | 2651 | try func.emitWValue(lhs); |
| ... | @@ -2854,7 +2909,7 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In | ... | @@ -2854,7 +2909,7 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In |
| 2854 | for (args) |operand| { | 2909 | for (args) |operand| { |
| 2855 | try func.emitWValue(operand); | 2910 | try func.emitWValue(operand); |
| 2856 | } | 2911 | } |
| 2857 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target.*) }); | 2912 | const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, pt, func.target) }); |
| 2858 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2913 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2859 | return .stack; | 2914 | return .stack; |
| 2860 | } | 2915 | } |
| ... | @@ -3141,8 +3196,8 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn | ... | @@ -3141,8 +3196,8 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn |
| 3141 | return .{ .imm32 = 0xaaaaaaaa }; | 3196 | return .{ .imm32 = 0xaaaaaaaa }; |
| 3142 | } | 3197 | } |
| 3143 | | 3198 | |
| 3144 | const atom_index = try func.bin_file.getOrCreateAtomForNav(pt, nav_index); | 3199 | const atom_index = try func.wasm.getOrCreateAtomForNav(pt, nav_index); |
| 3145 | const atom = func.bin_file.getAtom(atom_index); | 3200 | const atom = func.wasm.getAtom(atom_index); |
| 3146 | | 3201 | |
| 3147 | const target_sym_index = @intFromEnum(atom.sym_index); | 3202 | const target_sym_index = @intFromEnum(atom.sym_index); |
| 3148 | if (ip.isFunctionType(nav_ty)) { | 3203 | if (ip.isFunctionType(nav_ty)) { |
| ... | @@ -3156,7 +3211,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn | ... | @@ -3156,7 +3211,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn |
| 3156 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | 3211 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3157 | const pt = func.pt; | 3212 | const pt = func.pt; |
| 3158 | const zcu = pt.zcu; | 3213 | const zcu = pt.zcu; |
| 3159 | assert(!isByRef(ty, pt, func.target.*)); | 3214 | assert(!isByRef(ty, pt, func.target)); |
| 3160 | const ip = &zcu.intern_pool; | 3215 | const ip = &zcu.intern_pool; |
| 3161 | if (val.isUndefDeep(zcu)) return func.emitUndefined(ty); | 3216 | if (val.isUndefDeep(zcu)) return func.emitUndefined(ty); |
| 3162 | | 3217 | |
| ... | @@ -3267,7 +3322,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | ... | @@ -3267,7 +3322,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3267 | .aggregate => switch (ip.indexToKey(ty.ip_index)) { | 3322 | .aggregate => switch (ip.indexToKey(ty.ip_index)) { |
| 3268 | .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}), | 3323 | .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(pt)}), |
| 3269 | .vector_type => { | 3324 | .vector_type => { |
| 3270 | assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct); | 3325 | assert(determineSimdStoreStrategy(ty, zcu, func.target) == .direct); |
| 3271 | var buf: [16]u8 = undefined; | 3326 | var buf: [16]u8 = undefined; |
| 3272 | val.writeToMemory(pt, &buf) catch unreachable; | 3327 | val.writeToMemory(pt, &buf) catch unreachable; |
| 3273 | return func.storeSimdImmd(buf); | 3328 | return func.storeSimdImmd(buf); |
| ... | @@ -3398,11 +3453,11 @@ fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3398,11 +3453,11 @@ fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3398 | | 3453 | |
| 3399 | fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { | 3454 | fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { |
| 3400 | const pt = func.pt; | 3455 | const pt = func.pt; |
| 3401 | const wasm_block_ty = genBlockType(block_ty, pt, func.target.*); | 3456 | const wasm_block_ty = genBlockType(block_ty, pt, func.target); |
| 3402 | | 3457 | |
| 3403 | // if wasm_block_ty is non-empty, we create a register to store the temporary value | 3458 | // if wasm_block_ty is non-empty, we create a register to store the temporary value |
| 3404 | const block_result: WValue = if (wasm_block_ty != std.wasm.block_empty) blk: { | 3459 | const block_result: WValue = if (wasm_block_ty != std.wasm.block_empty) blk: { |
| 3405 | const ty: Type = if (isByRef(block_ty, pt, func.target.*)) Type.u32 else block_ty; | 3460 | const ty: Type = if (isByRef(block_ty, pt, func.target)) Type.u32 else block_ty; |
| 3406 | break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten | 3461 | break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten |
| 3407 | } else .none; | 3462 | } else .none; |
| 3408 | | 3463 | |
| ... | @@ -3527,7 +3582,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO | ... | @@ -3527,7 +3582,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3527 | } | 3582 | } |
| 3528 | } else if (ty.isAnyFloat()) { | 3583 | } else if (ty.isAnyFloat()) { |
| 3529 | return func.cmpFloat(ty, lhs, rhs, op); | 3584 | return func.cmpFloat(ty, lhs, rhs, op); |
| 3530 | } else if (isByRef(ty, pt, func.target.*)) { | 3585 | } else if (isByRef(ty, pt, func.target)) { |
| 3531 | return func.cmpBigInt(lhs, rhs, ty, op); | 3586 | return func.cmpBigInt(lhs, rhs, ty, op); |
| 3532 | } | 3587 | } |
| 3533 | | 3588 | |
| ... | @@ -3545,7 +3600,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO | ... | @@ -3545,7 +3600,7 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3545 | try func.lowerToStack(rhs); | 3600 | try func.lowerToStack(rhs); |
| 3546 | | 3601 | |
| 3547 | const opcode: std.wasm.Opcode = buildOpcode(.{ | 3602 | const opcode: std.wasm.Opcode = buildOpcode(.{ |
| 3548 | .valtype1 = typeToValtype(ty, pt, func.target.*), | 3603 | .valtype1 = typeToValtype(ty, pt, func.target), |
| 3549 | .op = switch (op) { | 3604 | .op = switch (op) { |
| 3550 | .lt => .lt, | 3605 | .lt => .lt, |
| 3551 | .lte => .le, | 3606 | .lte => .le, |
| ... | @@ -3612,7 +3667,7 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3612,7 +3667,7 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3612 | fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3667 | fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3613 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 3668 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3614 | const operand = try func.resolveInst(un_op); | 3669 | const operand = try func.resolveInst(un_op); |
| 3615 | const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null); | 3670 | const sym_index = try func.wasm.getGlobalSymbol("__zig_errors_len", null); |
| 3616 | const errors_len: WValue = .{ .memory = @intFromEnum(sym_index) }; | 3671 | const errors_len: WValue = .{ .memory = @intFromEnum(sym_index) }; |
| 3617 | | 3672 | |
| 3618 | try func.emitWValue(operand); | 3673 | try func.emitWValue(operand); |
| ... | @@ -3758,7 +3813,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3758,7 +3813,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3758 | break :result try func.bitcast(wanted_ty, given_ty, operand); | 3813 | break :result try func.bitcast(wanted_ty, given_ty, operand); |
| 3759 | } | 3814 | } |
| 3760 | | 3815 | |
| 3761 | if (isByRef(given_ty, pt, func.target.*) and !isByRef(wanted_ty, pt, func.target.*)) { | 3816 | if (isByRef(given_ty, pt, func.target) and !isByRef(wanted_ty, pt, func.target)) { |
| 3762 | const loaded_memory = try func.load(operand, wanted_ty, 0); | 3817 | const loaded_memory = try func.load(operand, wanted_ty, 0); |
| 3763 | if (needs_wrapping) { | 3818 | if (needs_wrapping) { |
| 3764 | break :result try func.wrapOperand(loaded_memory, wanted_ty); | 3819 | break :result try func.wrapOperand(loaded_memory, wanted_ty); |
| ... | @@ -3766,7 +3821,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3766,7 +3821,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3766 | break :result loaded_memory; | 3821 | break :result loaded_memory; |
| 3767 | } | 3822 | } |
| 3768 | } | 3823 | } |
| 3769 | if (!isByRef(given_ty, pt, func.target.*) and isByRef(wanted_ty, pt, func.target.*)) { | 3824 | if (!isByRef(given_ty, pt, func.target) and isByRef(wanted_ty, pt, func.target)) { |
| 3770 | const stack_memory = try func.allocStack(wanted_ty); | 3825 | const stack_memory = try func.allocStack(wanted_ty); |
| 3771 | try func.store(stack_memory, operand, given_ty, 0); | 3826 | try func.store(stack_memory, operand, given_ty, 0); |
| 3772 | if (needs_wrapping) { | 3827 | if (needs_wrapping) { |
| ... | @@ -3796,8 +3851,8 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn | ... | @@ -3796,8 +3851,8 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn |
| 3796 | | 3851 | |
| 3797 | const opcode = buildOpcode(.{ | 3852 | const opcode = buildOpcode(.{ |
| 3798 | .op = .reinterpret, | 3853 | .op = .reinterpret, |
| 3799 | .valtype1 = typeToValtype(wanted_ty, pt, func.target.*), | 3854 | .valtype1 = typeToValtype(wanted_ty, pt, func.target), |
| 3800 | .valtype2 = typeToValtype(given_ty, pt, func.target.*), | 3855 | .valtype2 = typeToValtype(given_ty, pt, func.target), |
| 3801 | }); | 3856 | }); |
| 3802 | try func.emitWValue(operand); | 3857 | try func.emitWValue(operand); |
| 3803 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 3858 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| ... | @@ -3919,8 +3974,8 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3919,8 +3974,8 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3919 | break :result try func.trunc(shifted_value, field_ty, backing_ty); | 3974 | break :result try func.trunc(shifted_value, field_ty, backing_ty); |
| 3920 | }, | 3975 | }, |
| 3921 | .@"union" => result: { | 3976 | .@"union" => result: { |
| 3922 | if (isByRef(struct_ty, pt, func.target.*)) { | 3977 | if (isByRef(struct_ty, pt, func.target)) { |
| 3923 | if (!isByRef(field_ty, pt, func.target.*)) { | 3978 | if (!isByRef(field_ty, pt, func.target)) { |
| 3924 | break :result try func.load(operand, field_ty, 0); | 3979 | break :result try func.load(operand, field_ty, 0); |
| 3925 | } else { | 3980 | } else { |
| 3926 | const new_stack_val = try func.allocStack(field_ty); | 3981 | const new_stack_val = try func.allocStack(field_ty); |
| ... | @@ -3946,7 +4001,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3946,7 +4001,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3946 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse { | 4001 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, zcu)) orelse { |
| 3947 | return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)}); | 4002 | return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(pt)}); |
| 3948 | }; | 4003 | }; |
| 3949 | if (isByRef(field_ty, pt, func.target.*)) { | 4004 | if (isByRef(field_ty, pt, func.target)) { |
| 3950 | switch (operand) { | 4005 | switch (operand) { |
| 3951 | .stack_offset => |stack_offset| { | 4006 | .stack_offset => |stack_offset| { |
| 3952 | break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } }; | 4007 | break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } }; |
| ... | @@ -4209,7 +4264,7 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo | ... | @@ -4209,7 +4264,7 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo |
| 4209 | } | 4264 | } |
| 4210 | | 4265 | |
| 4211 | const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); | 4266 | const pl_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))); |
| 4212 | if (op_is_ptr or isByRef(payload_ty, pt, func.target.*)) { | 4267 | if (op_is_ptr or isByRef(payload_ty, pt, func.target)) { |
| 4213 | break :result try func.buildPointerOffset(operand, pl_offset, .new); | 4268 | break :result try func.buildPointerOffset(operand, pl_offset, .new); |
| 4214 | } | 4269 | } |
| 4215 | | 4270 | |
| ... | @@ -4436,7 +4491,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4436,7 +4491,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4436 | const operand = try func.resolveInst(ty_op.operand); | 4491 | const operand = try func.resolveInst(ty_op.operand); |
| 4437 | if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand); | 4492 | if (opt_ty.optionalReprIsPayload(zcu)) break :result func.reuseOperand(ty_op.operand, operand); |
| 4438 | | 4493 | |
| 4439 | if (isByRef(payload_ty, pt, func.target.*)) { | 4494 | if (isByRef(payload_ty, pt, func.target)) { |
| 4440 | break :result try func.buildPointerOffset(operand, 0, .new); | 4495 | break :result try func.buildPointerOffset(operand, 0, .new); |
| 4441 | } | 4496 | } |
| 4442 | | 4497 | |
| ... | @@ -4570,7 +4625,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4570,7 +4625,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4570 | try func.addTag(.i32_mul); | 4625 | try func.addTag(.i32_mul); |
| 4571 | try func.addTag(.i32_add); | 4626 | try func.addTag(.i32_add); |
| 4572 | | 4627 | |
| 4573 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 4628 | const elem_result = if (isByRef(elem_ty, pt, func.target)) |
| 4574 | .stack | 4629 | .stack |
| 4575 | else | 4630 | else |
| 4576 | try func.load(.stack, elem_ty, 0); | 4631 | try func.load(.stack, elem_ty, 0); |
| ... | @@ -4729,7 +4784,7 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4729,7 +4784,7 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4729 | try func.addTag(.i32_mul); | 4784 | try func.addTag(.i32_mul); |
| 4730 | try func.addTag(.i32_add); | 4785 | try func.addTag(.i32_add); |
| 4731 | | 4786 | |
| 4732 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 4787 | const elem_result = if (isByRef(elem_ty, pt, func.target)) |
| 4733 | .stack | 4788 | .stack |
| 4734 | else | 4789 | else |
| 4735 | try func.load(.stack, elem_ty, 0); | 4790 | try func.load(.stack, elem_ty, 0); |
| ... | @@ -4780,7 +4835,7 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | ... | @@ -4780,7 +4835,7 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 4780 | else => ptr_ty.childType(zcu), | 4835 | else => ptr_ty.childType(zcu), |
| 4781 | }; | 4836 | }; |
| 4782 | | 4837 | |
| 4783 | const valtype = typeToValtype(Type.usize, pt, func.target.*); | 4838 | const valtype = typeToValtype(Type.usize, pt, func.target); |
| 4784 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); | 4839 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| 4785 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); | 4840 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| 4786 | | 4841 | |
| ... | @@ -4927,7 +4982,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4927,7 +4982,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4927 | const elem_ty = array_ty.childType(zcu); | 4982 | const elem_ty = array_ty.childType(zcu); |
| 4928 | const elem_size = elem_ty.abiSize(zcu); | 4983 | const elem_size = elem_ty.abiSize(zcu); |
| 4929 | | 4984 | |
| 4930 | if (isByRef(array_ty, pt, func.target.*)) { | 4985 | if (isByRef(array_ty, pt, func.target)) { |
| 4931 | try func.lowerToStack(array); | 4986 | try func.lowerToStack(array); |
| 4932 | try func.emitWValue(index); | 4987 | try func.emitWValue(index); |
| 4933 | try func.addImm32(@intCast(elem_size)); | 4988 | try func.addImm32(@intCast(elem_size)); |
| ... | @@ -4970,7 +5025,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4970,7 +5025,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4970 | } | 5025 | } |
| 4971 | } | 5026 | } |
| 4972 | | 5027 | |
| 4973 | const elem_result = if (isByRef(elem_ty, pt, func.target.*)) | 5028 | const elem_result = if (isByRef(elem_ty, pt, func.target)) |
| 4974 | .stack | 5029 | .stack |
| 4975 | else | 5030 | else |
| 4976 | try func.load(.stack, elem_ty, 0); | 5031 | try func.load(.stack, elem_ty, 0); |
| ... | @@ -5014,8 +5069,8 @@ fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5014,8 +5069,8 @@ fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5014 | try func.emitWValue(operand); | 5069 | try func.emitWValue(operand); |
| 5015 | const op = buildOpcode(.{ | 5070 | const op = buildOpcode(.{ |
| 5016 | .op = .trunc, | 5071 | .op = .trunc, |
| 5017 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | 5072 | .valtype1 = typeToValtype(dest_ty, pt, func.target), |
| 5018 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | 5073 | .valtype2 = typeToValtype(op_ty, pt, func.target), |
| 5019 | .signedness = dest_info.signedness, | 5074 | .signedness = dest_info.signedness, |
| 5020 | }); | 5075 | }); |
| 5021 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); | 5076 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| ... | @@ -5059,8 +5114,8 @@ fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5059,8 +5114,8 @@ fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5059 | try func.emitWValue(operand); | 5114 | try func.emitWValue(operand); |
| 5060 | const op = buildOpcode(.{ | 5115 | const op = buildOpcode(.{ |
| 5061 | .op = .convert, | 5116 | .op = .convert, |
| 5062 | .valtype1 = typeToValtype(dest_ty, pt, func.target.*), | 5117 | .valtype1 = typeToValtype(dest_ty, pt, func.target), |
| 5063 | .valtype2 = typeToValtype(op_ty, pt, func.target.*), | 5118 | .valtype2 = typeToValtype(op_ty, pt, func.target), |
| 5064 | .signedness = op_info.signedness, | 5119 | .signedness = op_info.signedness, |
| 5065 | }); | 5120 | }); |
| 5066 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); | 5121 | try func.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| ... | @@ -5076,7 +5131,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5076,7 +5131,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5076 | const ty = func.typeOfIndex(inst); | 5131 | const ty = func.typeOfIndex(inst); |
| 5077 | const elem_ty = ty.childType(zcu); | 5132 | const elem_ty = ty.childType(zcu); |
| 5078 | | 5133 | |
| 5079 | if (determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct) blk: { | 5134 | if (determineSimdStoreStrategy(ty, zcu, func.target) == .direct) blk: { |
| 5080 | switch (operand) { | 5135 | switch (operand) { |
| 5081 | // when the operand lives in the linear memory section, we can directly | 5136 | // when the operand lives in the linear memory section, we can directly |
| 5082 | // load and splat the value at once. Meaning we do not first have to load | 5137 | // load and splat the value at once. Meaning we do not first have to load |
| ... | @@ -5160,7 +5215,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5160,7 +5215,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5160 | const elem_size = child_ty.abiSize(zcu); | 5215 | const elem_size = child_ty.abiSize(zcu); |
| 5161 | | 5216 | |
| 5162 | // TODO: One of them could be by ref; handle in loop | 5217 | // TODO: One of them could be by ref; handle in loop |
| 5163 | if (isByRef(func.typeOf(extra.a), pt, func.target.*) or isByRef(inst_ty, pt, func.target.*)) { | 5218 | if (isByRef(func.typeOf(extra.a), pt, func.target) or isByRef(inst_ty, pt, func.target)) { |
| 5164 | const result = try func.allocStack(inst_ty); | 5219 | const result = try func.allocStack(inst_ty); |
| 5165 | | 5220 | |
| 5166 | for (0..mask_len) |index| { | 5221 | for (0..mask_len) |index| { |
| ... | @@ -5236,7 +5291,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5236,7 +5291,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5236 | // When the element type is by reference, we must copy the entire | 5291 | // When the element type is by reference, we must copy the entire |
| 5237 | // value. It is therefore safer to move the offset pointer and store | 5292 | // value. It is therefore safer to move the offset pointer and store |
| 5238 | // each value individually, instead of using store offsets. | 5293 | // each value individually, instead of using store offsets. |
| 5239 | if (isByRef(elem_ty, pt, func.target.*)) { | 5294 | if (isByRef(elem_ty, pt, func.target)) { |
| 5240 | // copy stack pointer into a temporary local, which is | 5295 | // copy stack pointer into a temporary local, which is |
| 5241 | // moved for each element to store each value in the right position. | 5296 | // moved for each element to store each value in the right position. |
| 5242 | const offset = try func.buildPointerOffset(result, 0, .new); | 5297 | const offset = try func.buildPointerOffset(result, 0, .new); |
| ... | @@ -5266,7 +5321,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5266,7 +5321,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5266 | }, | 5321 | }, |
| 5267 | .@"struct" => switch (result_ty.containerLayout(zcu)) { | 5322 | .@"struct" => switch (result_ty.containerLayout(zcu)) { |
| 5268 | .@"packed" => { | 5323 | .@"packed" => { |
| 5269 | if (isByRef(result_ty, pt, func.target.*)) { | 5324 | if (isByRef(result_ty, pt, func.target)) { |
| 5270 | return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); | 5325 | return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{}); |
| 5271 | } | 5326 | } |
| 5272 | const packed_struct = zcu.typeToPackedStruct(result_ty).?; | 5327 | const packed_struct = zcu.typeToPackedStruct(result_ty).?; |
| ... | @@ -5369,15 +5424,15 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5369,15 +5424,15 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5369 | if (layout.tag_size == 0) { | 5424 | if (layout.tag_size == 0) { |
| 5370 | break :result .none; | 5425 | break :result .none; |
| 5371 | } | 5426 | } |
| 5372 | assert(!isByRef(union_ty, pt, func.target.*)); | 5427 | assert(!isByRef(union_ty, pt, func.target)); |
| 5373 | break :result tag_int; | 5428 | break :result tag_int; |
| 5374 | } | 5429 | } |
| 5375 | | 5430 | |
| 5376 | if (isByRef(union_ty, pt, func.target.*)) { | 5431 | if (isByRef(union_ty, pt, func.target)) { |
| 5377 | const result_ptr = try func.allocStack(union_ty); | 5432 | const result_ptr = try func.allocStack(union_ty); |
| 5378 | const payload = try func.resolveInst(extra.init); | 5433 | const payload = try func.resolveInst(extra.init); |
| 5379 | if (layout.tag_align.compare(.gte, layout.payload_align)) { | 5434 | if (layout.tag_align.compare(.gte, layout.payload_align)) { |
| 5380 | if (isByRef(field_ty, pt, func.target.*)) { | 5435 | if (isByRef(field_ty, pt, func.target)) { |
| 5381 | const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); | 5436 | const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); |
| 5382 | try func.store(payload_ptr, payload, field_ty, 0); | 5437 | try func.store(payload_ptr, payload, field_ty, 0); |
| 5383 | } else { | 5438 | } else { |
| ... | @@ -5458,7 +5513,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: | ... | @@ -5458,7 +5513,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: |
| 5458 | | 5513 | |
| 5459 | _ = try func.load(lhs, payload_ty, 0); | 5514 | _ = try func.load(lhs, payload_ty, 0); |
| 5460 | _ = try func.load(rhs, payload_ty, 0); | 5515 | _ = try func.load(rhs, payload_ty, 0); |
| 5461 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target.*) }); | 5516 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, pt, func.target) }); |
| 5462 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 5517 | try func.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5463 | try func.addLabel(.br_if, 0); | 5518 | try func.addLabel(.br_if, 0); |
| 5464 | | 5519 | |
| ... | @@ -5910,7 +5965,7 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5910,7 +5965,7 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5910 | // As the names are global and the slice elements are constant, we do not have | 5965 | // As the names are global and the slice elements are constant, we do not have |
| 5911 | // to make a copy of the ptr+value but can point towards them directly. | 5966 | // to make a copy of the ptr+value but can point towards them directly. |
| 5912 | const pt = func.pt; | 5967 | const pt = func.pt; |
| 5913 | const error_table_symbol = try func.bin_file.getErrorTableSymbol(pt); | 5968 | const error_table_symbol = try func.wasm.getErrorTableSymbol(pt); |
| 5914 | const name_ty = Type.slice_const_u8_sentinel_0; | 5969 | const name_ty = Type.slice_const_u8_sentinel_0; |
| 5915 | const abi_size = name_ty.abiSize(pt.zcu); | 5970 | const abi_size = name_ty.abiSize(pt.zcu); |
| 5916 | | 5971 | |
| ... | @@ -5943,7 +5998,7 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE | ... | @@ -5943,7 +5998,7 @@ fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerE |
| 5943 | | 5998 | |
| 5944 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits | 5999 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits |
| 5945 | fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue { | 6000 | fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue { |
| 5946 | const zcu = func.bin_file.base.comp.zcu.?; | 6001 | const zcu = func.wasm.base.comp.zcu.?; |
| 5947 | const int_info = ty.intInfo(zcu); | 6002 | const int_info = ty.intInfo(zcu); |
| 5948 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 6003 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 5949 | return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); | 6004 | return func.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); |
| ... | @@ -6379,8 +6434,6 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6379,8 +6434,6 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6379 | } | 6434 | } |
| 6380 | | 6435 | |
| 6381 | fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6436 | fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6382 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | | |
| 6383 | | | |
| 6384 | const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; | 6437 | const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; |
| 6385 | try func.addInst(.{ .tag = .dbg_line, .data = .{ | 6438 | try func.addInst(.{ .tag = .dbg_line, .data = .{ |
| 6386 | .payload = try func.addExtra(Mir.DbgLineColumn{ | 6439 | .payload = try func.addExtra(Mir.DbgLineColumn{ |
| ... | @@ -6405,26 +6458,7 @@ fn airDbgVar( | ... | @@ -6405,26 +6458,7 @@ fn airDbgVar( |
| 6405 | is_ptr: bool, | 6458 | is_ptr: bool, |
| 6406 | ) InnerError!void { | 6459 | ) InnerError!void { |
| 6407 | _ = is_ptr; | 6460 | _ = is_ptr; |
| 6408 | if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); | 6461 | _ = local_tag; |
| 6409 | | | |
| 6410 | const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | | |
| 6411 | const ty = func.typeOf(pl_op.operand); | | |
| 6412 | const operand = try func.resolveInst(pl_op.operand); | | |
| 6413 | | | |
| 6414 | log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand }); | | |
| 6415 | | | |
| 6416 | const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); | | |
| 6417 | log.debug(" var name = ({s})", .{name.toSlice(func.air)}); | | |
| 6418 | | | |
| 6419 | const loc: link.File.Dwarf.Loc = switch (operand) { | | |
| 6420 | .local => |local| .{ .wasm_ext = .{ .local = local.value } }, | | |
| 6421 | else => blk: { | | |
| 6422 | log.debug("TODO generate debug info for {}", .{operand}); | | |
| 6423 | break :blk .empty; | | |
| 6424 | }, | | |
| 6425 | }; | | |
| 6426 | try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc); | | |
| 6427 | | | |
| 6428 | return func.finishAir(inst, .none, &.{}); | 6462 | return func.finishAir(inst, .none, &.{}); |
| 6429 | } | 6463 | } |
| 6430 | | 6464 | |
| ... | @@ -6500,7 +6534,7 @@ fn lowerTry( | ... | @@ -6500,7 +6534,7 @@ fn lowerTry( |
| 6500 | } | 6534 | } |
| 6501 | | 6535 | |
| 6502 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); | 6536 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); |
| 6503 | if (isByRef(pl_ty, pt, func.target.*)) { | 6537 | if (isByRef(pl_ty, pt, func.target)) { |
| 6504 | return buildPointerOffset(func, err_union, pl_offset, .new); | 6538 | return buildPointerOffset(func, err_union, pl_offset, .new); |
| 6505 | } | 6539 | } |
| 6506 | const payload = try func.load(err_union, pl_ty, pl_offset); | 6540 | const payload = try func.load(err_union, pl_ty, pl_offset); |
| ... | @@ -7074,15 +7108,15 @@ fn callIntrinsic( | ... | @@ -7074,15 +7108,15 @@ fn callIntrinsic( |
| 7074 | args: []const WValue, | 7108 | args: []const WValue, |
| 7075 | ) InnerError!WValue { | 7109 | ) InnerError!WValue { |
| 7076 | assert(param_types.len == args.len); | 7110 | assert(param_types.len == args.len); |
| 7077 | const wasm = func.bin_file; | 7111 | const wasm = func.wasm; |
| 7078 | const pt = func.pt; | 7112 | const pt = func.pt; |
| 7079 | const zcu = pt.zcu; | 7113 | const zcu = pt.zcu; |
| 7080 | const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*); | 7114 | const func_type_index = try genFunctype(wasm, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target); |
| 7081 | const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index); | 7115 | const func_index = wasm.getOutputFunction(try wasm.internString(name), func_type_index); |
| 7082 | | 7116 | |
| 7083 | // Always pass over C-ABI | 7117 | // Always pass over C-ABI |
| 7084 | | 7118 | |
| 7085 | const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*); | 7119 | const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target); |
| 7086 | // if we want return as first param, we allocate a pointer to stack, | 7120 | // if we want return as first param, we allocate a pointer to stack, |
| 7087 | // and emit it as our first argument | 7121 | // and emit it as our first argument |
| 7088 | const sret = if (want_sret_param) blk: { | 7122 | const sret = if (want_sret_param) blk: { |
| ... | @@ -7121,7 +7155,7 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7121,7 +7155,7 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7121 | const result_ptr = try func.allocStack(func.typeOfIndex(inst)); | 7155 | const result_ptr = try func.allocStack(func.typeOfIndex(inst)); |
| 7122 | try func.lowerToStack(result_ptr); | 7156 | try func.lowerToStack(result_ptr); |
| 7123 | try func.emitWValue(operand); | 7157 | try func.emitWValue(operand); |
| 7124 | try func.addCallTagName(enum_ty.toIntern()); | 7158 | try func.addIpIndex(.call_tag_name, enum_ty.toIntern()); |
| 7125 | | 7159 | |
| 7126 | return func.finishAir(inst, result_ptr, &.{un_op}); | 7160 | return func.finishAir(inst, result_ptr, &.{un_op}); |
| 7127 | } | 7161 | } |
| ... | @@ -7265,7 +7299,7 @@ fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7265,7 +7299,7 @@ fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7265 | break :val ptr_val; | 7299 | break :val ptr_val; |
| 7266 | }; | 7300 | }; |
| 7267 | | 7301 | |
| 7268 | const result = if (isByRef(result_ty, pt, func.target.*)) val: { | 7302 | const result = if (isByRef(result_ty, pt, func.target)) val: { |
| 7269 | try func.emitWValue(cmp_result); | 7303 | try func.emitWValue(cmp_result); |
| 7270 | try func.addImm32(~@as(u32, 0)); | 7304 | try func.addImm32(~@as(u32, 0)); |
| 7271 | try func.addTag(.i32_xor); | 7305 | try func.addTag(.i32_xor); |