authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-05 23:46:46-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
loge521879e4730fd85a92081c0040db7dc5daad8a3
treebe07a1a575ad2728027846e0b8cce0f50b807cf0
parentb9355edfb1db042098bc232cf8e52e079f4fcf4e

rewrite wasm/Emit.zig

mainly, rework how relocations works. This is the point at which symbol indexes are known - not before. And don't emit unnecessary relocations! They're only needed when emitting an object file. Changes wasm linker to keep MIR around long-lived so that fixups can be reapplied after linker garbage collection. use labeled switch while we're at it

11 files changed, 1075 insertions(+), 1118 deletions(-)

src/arch/wasm/CodeGen.zig+366-332
...@@ -7,6 +7,7 @@ const leb = std.leb;...@@ -7,6 +7,7 @@ const leb = std.leb;
7const mem = std.mem;7const mem = std.mem;
8const log = std.log.scoped(.codegen);8const log = std.log.scoped(.codegen);
99
10const CodeGen = @This();
10const codegen = @import("../../codegen.zig");11const codegen = @import("../../codegen.zig");
11const Zcu = @import("../../Zcu.zig");12const Zcu = @import("../../Zcu.zig");
12const InternPool = @import("../../InternPool.zig");13const InternPool = @import("../../InternPool.zig");
...@@ -24,6 +25,98 @@ const abi = @import("abi.zig");...@@ -24,6 +25,98 @@ const abi = @import("abi.zig");
24const Alignment = InternPool.Alignment;25const Alignment = InternPool.Alignment;
25const errUnionPayloadOffset = codegen.errUnionPayloadOffset;26const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
26const errUnionErrorOffset = codegen.errUnionErrorOffset;27const errUnionErrorOffset = codegen.errUnionErrorOffset;
28const Wasm = link.File.Wasm;
29
30/// Reference to the function declaration the code
31/// section belongs to
32owner_nav: InternPool.Nav.Index,
33/// Current block depth. Used to calculate the relative difference between a break
34/// and block
35block_depth: u32 = 0,
36air: Air,
37liveness: Liveness,
38gpa: mem.Allocator,
39func_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.
44branches: 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
48blocks: 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.
53loops: 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
57local_index: u32,
58/// The index of the current argument.
59/// Used to track which argument is being referenced in `airArg`.
60arg_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.
64simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
65/// The Target we're emitting (used to call intInfo)
66target: *const std.Target,
67wasm: *link.File.Wasm,
68pt: Zcu.PerThread,
69/// List of MIR Instructions
70mir_instructions: *std.MultiArrayList(Mir.Inst),
71/// Contains extra data for MIR
72mir_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.
75locals: *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.
78initial_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.
81bottom_stack_value: WValue = .none,
82/// Arguments of this function declaration
83/// This will be set after `resolveCallingConventionValues`
84args: []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.
88return_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`.
91stack_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.
96stack_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.
102free_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.
105free_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.
108free_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.
111free_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.
114free_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.
119air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
27120
28/// Wasm Value, created when generating an instruction121/// Wasm Value, created when generating an instruction
29const WValue = union(enum) {122const 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`
602pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);695pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
603696
604const CodeGen = @This();
605
606/// Reference to the function declaration the code
607/// section belongs to
608owner_nav: InternPool.Nav.Index,
609src_loc: Zcu.LazySrcLoc,
610/// Current block depth. Used to calculate the relative difference between a break
611/// and block
612block_depth: u32 = 0,
613air: Air,
614liveness: Liveness,
615gpa: mem.Allocator,
616debug_output: link.File.DebugInfoOutput,
617func_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.
622branches: 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
626blocks: 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.
631loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
632/// `bytes` contains the wasm bytecode belonging to the 'code' section.
633code: *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
637local_index: u32 = 0,
638/// The index of the current argument.
639/// Used to track which argument is being referenced in `airArg`.
640arg_index: u32 = 0,
641/// List of all locals' types generated throughout this declaration
642/// used to emit locals count at start of 'code' section.
643locals: 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.
647simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty,
648/// The Target we're emitting (used to call intInfo)
649target: *const std.Target,
650/// Represents the wasm binary file that is being linked.
651bin_file: *link.File.Wasm,
652pt: Zcu.PerThread,
653/// List of MIR Instructions
654mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
655/// Contains extra data for MIR
656mir_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.
659initial_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.
662bottom_stack_value: WValue = .none,
663/// Arguments of this function declaration
664/// This will be set after `resolveCallingConventionValues`
665args: []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.
669return_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`.
672stack_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.
677stack_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.
683free_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.
686free_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.
689free_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.
692free_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.
695free_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.
700air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
701
702const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};697const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
703698
704const InnerError = error{699const 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}
731724
732fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {725fn 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}
736730
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 fits762 // 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 else766 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}
887881
888fn addCallTagName(func: *CodeGen, ip_index: InternPool.Index) error{OutOfMemory}!void {882fn 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
886fn 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}
891889
892/// Accepts an unsigned 32bit integer rather than a signed integer to890/// 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 values898/// 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.
902fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {900fn 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}
906904
...@@ -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}
917915
918fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {916fn 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}
922920
...@@ -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}
964964
965/// Using a given `Type`, returns the corresponding valtype for .auto callconv965/// Using a given `Type`, returns the corresponding valtype for .auto callconv
966fn typeToValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) std.wasm.Valtype {966fn 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 u16971 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}
10041004
1005/// Using a given `Type`, returns the byte representation of its wasm value type1005/// Using a given `Type`, returns the byte representation of its wasm value type
1006fn genValtype(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {1006fn 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}
10091009
1010/// Using a given `Type`, returns the corresponding wasm value type1010/// Using a given `Type`, returns the corresponding wasm value type
1011/// Differently from `genValtype` this also allows `void` to create a block1011/// Differently from `genValtype` this also allows `void` to create a block
1012/// with no return type1012/// with no return type
1013fn genBlockType(ty: Type, pt: Zcu.PerThread, target: std.Target) u8 {1013fn 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 relocation1039 },
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 offset1042 .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 tag1077/// Returns a corresponding `Wvalue` with `local` as active tag
1076fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {1078fn 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.
1096fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {1098fn 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}
11641166
1165pub fn generate(1167pub 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
1261pub 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
1269pub 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();
11951308
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}
12011314
1202fn genFunc(func: *CodeGen) InnerError!void {1315fn 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.*);
12101319
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);
12131323
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 branch1325 // 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 merge1329 assert(cg.branches.items.len == 0); // missing branch merge
1225 }1330 }
1226 // Generate MIR for function body1331 // Generate MIR for function body
1227 try func.genBody(func.air.getMainBody());1332 try cg.genBody(cg.air.getMainBody());
12281333
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 body1343 // End of function body
1240 try func.addTag(.end);1344 try cg.addTag(.end);
12411345 try cg.addTag(.dbg_epilogue_begin);
1242 try func.addTag(.dbg_epilogue_begin);1346
12431347 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,
12491353 .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 pointer1355 .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 function1357 .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}
13051362
1306const CallWValues = struct {1363const CallWValues = struct {
1307 args: []WValue,1364 args: []WValue,
1308 return_value: WValue,1365 return_value: WValue,
1366 local_index: u32,
13091367
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};
13151373
1316fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {1374fn 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;
13271391
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();
13301394
1331 // Check if we store the result as a pointer to the stack rather than1395 // Check if we store the result as a pointer to the stack rather than
1332 // by value1396 // 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 we1398 // 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 }
13391403
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 }
13461410
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}
13661430
1367fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, pt: Zcu.PerThread, target: std.Target) bool {1431fn 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 value1535 // Get the original stack pointer's value
1467 try func.emitWValue(func.initial_stack_value);1536 try func.emitWValue(func.initial_stack_value);
14681537
1469 // save its value in the global stack pointer1538 try func.addTag(.global_set_sp);
1470 try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym));
1471}1539}
14721540
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 {
16751743
1676/// For a given `Type`, will return true when the type will be passed1744/// For a given `Type`, will return true when the type will be passed
1677/// by reference, rather than by value1745/// by reference, rather than by value
1678fn isByRef(ty: Type, pt: Zcu.PerThread, target: std.Target) bool {1746fn 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-simd1815/// 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 store1816/// 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.
1750fn determineSimdStoreStrategy(ty: Type, zcu: *Zcu, target: std.Target) SimdStoreStrategy {1818fn 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 }
21102178
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 }
21152183
...@@ -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 stack2203 // 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}
21432211
2144fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {2212fn 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);
21632231
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);
22012269
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 }
22052273
...@@ -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);
22622330
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 }
22662334
...@@ -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 instead2456 // into lhs, so we calculate that and emit that instead
2389 try func.lowerToStack(rhs);2457 try func.lowerToStack(rhs);
23902458
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});
24182486
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
24672535
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 }
25192587
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}
25352590
...@@ -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 }
25792634
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!
25902645
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 }
31433198
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);
31463201
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
3156fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {3211fn 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);
31623217
...@@ -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 {
33983453
3399fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {3454fn 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);
34023457
3403 // if wasm_block_ty is non-empty, we create a register to store the temporary value3458 // 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 overwritten3461 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;
34083463
...@@ -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 }
35333588
...@@ -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);
35463601
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 {
3612fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3667fn 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) };
36173672
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 }
37603815
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
37963851
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 }
42104265
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 }
42154270
...@@ -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);
44384493
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 }
44424497
...@@ -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);
45724627
4573 const elem_result = if (isByRef(elem_ty, pt, func.target.*))4628 const elem_result = if (isByRef(elem_ty, pt, func.target))
4574 .stack4629 .stack
4575 else4630 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);
47314786
4732 const elem_result = if (isByRef(elem_ty, pt, func.target.*))4787 const elem_result = if (isByRef(elem_ty, pt, func.target))
4733 .stack4788 .stack
4734 else4789 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 };
47824837
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 });
47864841
...@@ -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);
49294984
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 }
49725027
4973 const elem_result = if (isByRef(elem_ty, pt, func.target.*))5028 const elem_result = if (isByRef(elem_ty, pt, func.target))
4974 .stack5029 .stack
4975 else5030 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);
50785133
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 directly5136 // 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 load5137 // 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);
51615216
5162 // TODO: One of them could be by ref; handle in loop5217 // 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);
51655220
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 entire5291 // When the element type is by reference, we must copy the entire
5237 // value. It is therefore safer to move the offset pointer and store5292 // 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 is5295 // 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 }
53755430
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:
54585513
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);
54645519
...@@ -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 have5965 // 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);
59165971
...@@ -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
59435998
5944/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits5999/// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits
5945fn intZeroValue(func: *CodeGen, ty: Type) InnerError!WValue {6000fn 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}
63806435
6381fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6436fn 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}
64306464
...@@ -6500,7 +6534,7 @@ fn lowerTry(...@@ -6500,7 +6534,7 @@ fn lowerTry(
6500 }6534 }
65016535
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);
70827116
7083 // Always pass over C-ABI7117 // Always pass over C-ABI
70847118
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 argument7121 // 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());
71257159
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 };
72677301
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);
src/arch/wasm/Emit.zig+569-618
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1//! Contains all logic to lower wasm MIR into its binary
2//! or textual representation.
3
4const Emit = @This();1const Emit = @This();
2
5const std = @import("std");3const std = @import("std");
6const leb128 = std.leb;4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const leb = std.leb;
77
8const Mir = @import("Mir.zig");8const Mir = @import("Mir.zig");
9const link = @import("../../link.zig");9const link = @import("../../link.zig");
...@@ -11,660 +11,611 @@ const Zcu = @import("../../Zcu.zig");...@@ -11,660 +11,611 @@ const Zcu = @import("../../Zcu.zig");
11const InternPool = @import("../../InternPool.zig");11const InternPool = @import("../../InternPool.zig");
12const codegen = @import("../../codegen.zig");12const codegen = @import("../../codegen.zig");
1313
14/// Contains our list of instructions
15mir: Mir,14mir: Mir,
16/// Reference to the Wasm module linker15wasm: *link.File.Wasm,
17bin_file: *link.File.Wasm,16/// The binary representation that will be emitted by this module.
18/// Possible error message. When set, the value is allocated and17code: *std.ArrayListUnmanaged(u8),
19/// must be freed manually.18
20error_msg: ?*Zcu.ErrorMsg = null,19pub const Error = error{
21/// The binary representation that will be emit by this module.
22code: *std.ArrayList(u8),
23/// List of allocated locals.
24locals: []const u8,
25/// The declaration that code is being generated for.
26owner_nav: InternPool.Nav.Index,
27
28// Debug information
29/// Holds the debug information for this emission
30dbg_output: link.File.DebugInfoOutput,
31/// Previous debug info line
32prev_di_line: u32,
33/// Previous debug info column
34prev_di_column: u32,
35/// Previous offset relative to code section
36prev_di_offset: u32,
37
38const InnerError = error{
39 OutOfMemory,20 OutOfMemory,
40 EmitFail,
41};21};
4222
43pub fn emitMir(emit: *Emit) InnerError!void {23pub fn lowerToCode(emit: *Emit) Error!void {
44 const mir_tags = emit.mir.instructions.items(.tag);24 const mir = &emit.mir;
45 // write the locals in the prologue of the function body25 const code = emit.code;
46 // before we emit the function body when lowering MIR26 const wasm = emit.wasm;
47 try emit.emitLocals();
48
49 for (mir_tags, 0..) |tag, index| {
50 const inst = @as(u32, @intCast(index));
51 switch (tag) {
52 // block instructions
53 .block => try emit.emitBlock(tag, inst),
54 .loop => try emit.emitBlock(tag, inst),
55
56 .dbg_line => try emit.emitDbgLine(inst),
57 .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(),
58 .dbg_prologue_end => try emit.emitDbgPrologueEnd(),
59
60 // branch instructions
61 .br_if => try emit.emitLabel(tag, inst),
62 .br_table => try emit.emitBrTable(inst),
63 .br => try emit.emitLabel(tag, inst),
64
65 // relocatables
66 .call => try emit.emitCall(inst),
67 .call_indirect => try emit.emitCallIndirect(inst),
68 .global_get => try emit.emitGlobal(tag, inst),
69 .global_set => try emit.emitGlobal(tag, inst),
70 .function_index => try emit.emitFunctionIndex(inst),
71 .memory_address => try emit.emitMemAddress(inst),
72
73 // immediates
74 .f32_const => try emit.emitFloat32(inst),
75 .f64_const => try emit.emitFloat64(inst),
76 .i32_const => try emit.emitImm32(inst),
77 .i64_const => try emit.emitImm64(inst),
78
79 // memory instructions
80 .i32_load => try emit.emitMemArg(tag, inst),
81 .i64_load => try emit.emitMemArg(tag, inst),
82 .f32_load => try emit.emitMemArg(tag, inst),
83 .f64_load => try emit.emitMemArg(tag, inst),
84 .i32_load8_s => try emit.emitMemArg(tag, inst),
85 .i32_load8_u => try emit.emitMemArg(tag, inst),
86 .i32_load16_s => try emit.emitMemArg(tag, inst),
87 .i32_load16_u => try emit.emitMemArg(tag, inst),
88 .i64_load8_s => try emit.emitMemArg(tag, inst),
89 .i64_load8_u => try emit.emitMemArg(tag, inst),
90 .i64_load16_s => try emit.emitMemArg(tag, inst),
91 .i64_load16_u => try emit.emitMemArg(tag, inst),
92 .i64_load32_s => try emit.emitMemArg(tag, inst),
93 .i64_load32_u => try emit.emitMemArg(tag, inst),
94 .i32_store => try emit.emitMemArg(tag, inst),
95 .i64_store => try emit.emitMemArg(tag, inst),
96 .f32_store => try emit.emitMemArg(tag, inst),
97 .f64_store => try emit.emitMemArg(tag, inst),
98 .i32_store8 => try emit.emitMemArg(tag, inst),
99 .i32_store16 => try emit.emitMemArg(tag, inst),
100 .i64_store8 => try emit.emitMemArg(tag, inst),
101 .i64_store16 => try emit.emitMemArg(tag, inst),
102 .i64_store32 => try emit.emitMemArg(tag, inst),
103
104 // Instructions with an index that do not require relocations
105 .local_get => try emit.emitLabel(tag, inst),
106 .local_set => try emit.emitLabel(tag, inst),
107 .local_tee => try emit.emitLabel(tag, inst),
108 .memory_grow => try emit.emitLabel(tag, inst),
109 .memory_size => try emit.emitLabel(tag, inst),
110
111 // no-ops
112 .end => try emit.emitTag(tag),
113 .@"return" => try emit.emitTag(tag),
114 .@"unreachable" => try emit.emitTag(tag),
115
116 .select => try emit.emitTag(tag),
117
118 // arithmetic
119 .i32_eqz => try emit.emitTag(tag),
120 .i32_eq => try emit.emitTag(tag),
121 .i32_ne => try emit.emitTag(tag),
122 .i32_lt_s => try emit.emitTag(tag),
123 .i32_lt_u => try emit.emitTag(tag),
124 .i32_gt_s => try emit.emitTag(tag),
125 .i32_gt_u => try emit.emitTag(tag),
126 .i32_le_s => try emit.emitTag(tag),
127 .i32_le_u => try emit.emitTag(tag),
128 .i32_ge_s => try emit.emitTag(tag),
129 .i32_ge_u => try emit.emitTag(tag),
130 .i64_eqz => try emit.emitTag(tag),
131 .i64_eq => try emit.emitTag(tag),
132 .i64_ne => try emit.emitTag(tag),
133 .i64_lt_s => try emit.emitTag(tag),
134 .i64_lt_u => try emit.emitTag(tag),
135 .i64_gt_s => try emit.emitTag(tag),
136 .i64_gt_u => try emit.emitTag(tag),
137 .i64_le_s => try emit.emitTag(tag),
138 .i64_le_u => try emit.emitTag(tag),
139 .i64_ge_s => try emit.emitTag(tag),
140 .i64_ge_u => try emit.emitTag(tag),
141 .f32_eq => try emit.emitTag(tag),
142 .f32_ne => try emit.emitTag(tag),
143 .f32_lt => try emit.emitTag(tag),
144 .f32_gt => try emit.emitTag(tag),
145 .f32_le => try emit.emitTag(tag),
146 .f32_ge => try emit.emitTag(tag),
147 .f64_eq => try emit.emitTag(tag),
148 .f64_ne => try emit.emitTag(tag),
149 .f64_lt => try emit.emitTag(tag),
150 .f64_gt => try emit.emitTag(tag),
151 .f64_le => try emit.emitTag(tag),
152 .f64_ge => try emit.emitTag(tag),
153 .i32_add => try emit.emitTag(tag),
154 .i32_sub => try emit.emitTag(tag),
155 .i32_mul => try emit.emitTag(tag),
156 .i32_div_s => try emit.emitTag(tag),
157 .i32_div_u => try emit.emitTag(tag),
158 .i32_and => try emit.emitTag(tag),
159 .i32_or => try emit.emitTag(tag),
160 .i32_xor => try emit.emitTag(tag),
161 .i32_shl => try emit.emitTag(tag),
162 .i32_shr_s => try emit.emitTag(tag),
163 .i32_shr_u => try emit.emitTag(tag),
164 .i64_add => try emit.emitTag(tag),
165 .i64_sub => try emit.emitTag(tag),
166 .i64_mul => try emit.emitTag(tag),
167 .i64_div_s => try emit.emitTag(tag),
168 .i64_div_u => try emit.emitTag(tag),
169 .i64_and => try emit.emitTag(tag),
170 .i64_or => try emit.emitTag(tag),
171 .i64_xor => try emit.emitTag(tag),
172 .i64_shl => try emit.emitTag(tag),
173 .i64_shr_s => try emit.emitTag(tag),
174 .i64_shr_u => try emit.emitTag(tag),
175 .f32_abs => try emit.emitTag(tag),
176 .f32_neg => try emit.emitTag(tag),
177 .f32_ceil => try emit.emitTag(tag),
178 .f32_floor => try emit.emitTag(tag),
179 .f32_trunc => try emit.emitTag(tag),
180 .f32_nearest => try emit.emitTag(tag),
181 .f32_sqrt => try emit.emitTag(tag),
182 .f32_add => try emit.emitTag(tag),
183 .f32_sub => try emit.emitTag(tag),
184 .f32_mul => try emit.emitTag(tag),
185 .f32_div => try emit.emitTag(tag),
186 .f32_min => try emit.emitTag(tag),
187 .f32_max => try emit.emitTag(tag),
188 .f32_copysign => try emit.emitTag(tag),
189 .f64_abs => try emit.emitTag(tag),
190 .f64_neg => try emit.emitTag(tag),
191 .f64_ceil => try emit.emitTag(tag),
192 .f64_floor => try emit.emitTag(tag),
193 .f64_trunc => try emit.emitTag(tag),
194 .f64_nearest => try emit.emitTag(tag),
195 .f64_sqrt => try emit.emitTag(tag),
196 .f64_add => try emit.emitTag(tag),
197 .f64_sub => try emit.emitTag(tag),
198 .f64_mul => try emit.emitTag(tag),
199 .f64_div => try emit.emitTag(tag),
200 .f64_min => try emit.emitTag(tag),
201 .f64_max => try emit.emitTag(tag),
202 .f64_copysign => try emit.emitTag(tag),
203 .i32_wrap_i64 => try emit.emitTag(tag),
204 .i64_extend_i32_s => try emit.emitTag(tag),
205 .i64_extend_i32_u => try emit.emitTag(tag),
206 .i32_extend8_s => try emit.emitTag(tag),
207 .i32_extend16_s => try emit.emitTag(tag),
208 .i64_extend8_s => try emit.emitTag(tag),
209 .i64_extend16_s => try emit.emitTag(tag),
210 .i64_extend32_s => try emit.emitTag(tag),
211 .f32_demote_f64 => try emit.emitTag(tag),
212 .f64_promote_f32 => try emit.emitTag(tag),
213 .i32_reinterpret_f32 => try emit.emitTag(tag),
214 .i64_reinterpret_f64 => try emit.emitTag(tag),
215 .f32_reinterpret_i32 => try emit.emitTag(tag),
216 .f64_reinterpret_i64 => try emit.emitTag(tag),
217 .i32_trunc_f32_s => try emit.emitTag(tag),
218 .i32_trunc_f32_u => try emit.emitTag(tag),
219 .i32_trunc_f64_s => try emit.emitTag(tag),
220 .i32_trunc_f64_u => try emit.emitTag(tag),
221 .i64_trunc_f32_s => try emit.emitTag(tag),
222 .i64_trunc_f32_u => try emit.emitTag(tag),
223 .i64_trunc_f64_s => try emit.emitTag(tag),
224 .i64_trunc_f64_u => try emit.emitTag(tag),
225 .f32_convert_i32_s => try emit.emitTag(tag),
226 .f32_convert_i32_u => try emit.emitTag(tag),
227 .f32_convert_i64_s => try emit.emitTag(tag),
228 .f32_convert_i64_u => try emit.emitTag(tag),
229 .f64_convert_i32_s => try emit.emitTag(tag),
230 .f64_convert_i32_u => try emit.emitTag(tag),
231 .f64_convert_i64_s => try emit.emitTag(tag),
232 .f64_convert_i64_u => try emit.emitTag(tag),
233 .i32_rem_s => try emit.emitTag(tag),
234 .i32_rem_u => try emit.emitTag(tag),
235 .i64_rem_s => try emit.emitTag(tag),
236 .i64_rem_u => try emit.emitTag(tag),
237 .i32_popcnt => try emit.emitTag(tag),
238 .i64_popcnt => try emit.emitTag(tag),
239 .i32_clz => try emit.emitTag(tag),
240 .i32_ctz => try emit.emitTag(tag),
241 .i64_clz => try emit.emitTag(tag),
242 .i64_ctz => try emit.emitTag(tag),
243
244 .misc_prefix => try emit.emitExtended(inst),
245 .simd_prefix => try emit.emitSimd(inst),
246 .atomics_prefix => try emit.emitAtomic(inst),
247 }
248 }
249}
250
251fn offset(self: Emit) u32 {
252 return @as(u32, @intCast(self.code.items.len));
253}
254
255fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
256 @branchHint(.cold);
257 std.debug.assert(emit.error_msg == null);
258 const wasm = emit.bin_file;
259 const comp = wasm.base.comp;27 const comp = wasm.base.comp;
260 const zcu = comp.zcu.?;
261 const gpa = comp.gpa;28 const gpa = comp.gpa;
262 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(emit.owner_nav), format, args);29 const is_obj = comp.config.output_mode == .Obj;
263 return error.EmitFail;
264}
265
266fn emitLocals(emit: *Emit) !void {
267 const writer = emit.code.writer();
268 try leb128.writeUleb128(writer, @as(u32, @intCast(emit.locals.len)));
269 // emit the actual locals amount
270 for (emit.locals) |local| {
271 try leb128.writeUleb128(writer, @as(u32, 1));
272 try writer.writeByte(local);
273 }
274}
27530
276fn emitTag(emit: *Emit, tag: Mir.Inst.Tag) !void {31 const tags = mir.instructions.items(.tag);
277 try emit.code.append(@intFromEnum(tag));32 const datas = mir.instructions.items(.data);
278}33 var inst: u32 = 0;
27934
280fn emitBlock(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {35 loop: switch (tags[inst]) {
281 const block_type = emit.mir.instructions.items(.data)[inst].block_type;36 .block, .loop => {
282 try emit.code.append(@intFromEnum(tag));37 const block_type = datas[inst].block_type;
283 try emit.code.append(block_type);38 try code.ensureUnusedCapacity(gpa, 2);
284}39 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
40 code.appendAssumeCapacity(block_type);
28541
286fn emitBrTable(emit: *Emit, inst: Mir.Inst.Index) !void {42 inst += 1;
287 const extra_index = emit.mir.instructions.items(.data)[inst].payload;43 continue :loop tags[inst];
288 const extra = emit.mir.extraData(Mir.JumpTable, extra_index);44 },
289 const labels = emit.mir.extra[extra.end..][0..extra.data.length];
290 const writer = emit.code.writer();
29145
292 try emit.code.append(@intFromEnum(std.wasm.Opcode.br_table));46 .uav_ref => {
293 try leb128.writeUleb128(writer, extra.data.length - 1); // Default label is not part of length/depth47 try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
294 for (labels) |label| {
295 try leb128.writeUleb128(writer, label);
296 }
297}
29848
299fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {49 inst += 1;
300 const label = emit.mir.instructions.items(.data)[inst].label;50 continue :loop tags[inst];
301 try emit.code.append(@intFromEnum(tag));51 },
302 try leb128.writeUleb128(emit.code.writer(), label);52 .uav_ref_off => {
303}53 try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);
30454
305fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {55 inst += 1;
306 const wasm = emit.bin_file;56 continue :loop tags[inst];
307 const comp = wasm.base.comp;57 },
308 const gpa = comp.gpa;
309 const label = emit.mir.instructions.items(.data)[inst].label;
310 try emit.code.append(@intFromEnum(tag));
311 var buf: [5]u8 = undefined;
312 leb128.writeUnsignedFixed(5, &buf, label);
313 const global_offset = emit.offset();
314 try emit.code.appendSlice(&buf);
315
316 const zo = wasm.zig_object.?;
317 try zo.relocs.append(gpa, .{
318 .nav_index = emit.nav_index,
319 .index = label,
320 .offset = global_offset,
321 .tag = .GLOBAL_INDEX_LEB,
322 });
323}
32458
325fn emitImm32(emit: *Emit, inst: Mir.Inst.Index) !void {59 .dbg_line => {
326 const value: i32 = emit.mir.instructions.items(.data)[inst].imm32;60 inst += 1;
327 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));61 continue :loop tags[inst];
328 try leb128.writeIleb128(emit.code.writer(), value);62 },
329}63 .dbg_epilogue_begin => {
64 return;
65 },
33066
331fn emitImm64(emit: *Emit, inst: Mir.Inst.Index) !void {67 .br_if, .br, .memory_grow, .memory_size => {
332 const extra_index = emit.mir.instructions.items(.data)[inst].payload;68 try code.ensureUnusedCapacity(gpa, 11);
333 const value = emit.mir.extraData(Mir.Imm64, extra_index);69 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
334 try emit.code.append(@intFromEnum(std.wasm.Opcode.i64_const));70 leb.writeUleb128(code.fixedWriter(), datas[inst].label) catch unreachable;
335 try leb128.writeIleb128(emit.code.writer(), @as(i64, @bitCast(value.data.toU64())));
336}
33771
338fn emitFloat32(emit: *Emit, inst: Mir.Inst.Index) !void {72 inst += 1;
339 const value: f32 = emit.mir.instructions.items(.data)[inst].float32;73 continue :loop tags[inst];
340 try emit.code.append(@intFromEnum(std.wasm.Opcode.f32_const));74 },
341 try emit.code.writer().writeInt(u32, @bitCast(value), .little);
342}
34375
344fn emitFloat64(emit: *Emit, inst: Mir.Inst.Index) !void {76 .local_get, .local_set, .local_tee => {
345 const extra_index = emit.mir.instructions.items(.data)[inst].payload;77 try code.ensureUnusedCapacity(gpa, 11);
346 const value = emit.mir.extraData(Mir.Float64, extra_index);78 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
347 try emit.code.append(@intFromEnum(std.wasm.Opcode.f64_const));79 leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable;
348 try emit.code.writer().writeInt(u64, value.data.toU64(), .little);
349}
35080
351fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {81 inst += 1;
352 const extra_index = emit.mir.instructions.items(.data)[inst].payload;82 continue :loop tags[inst];
353 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;83 },
354 try emit.code.append(@intFromEnum(tag));
355 try encodeMemArg(mem_arg, emit.code.writer());
356}
35784
358fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {85 .br_table => {
359 // wasm encodes alignment as power of 2, rather than natural alignment86 const extra_index = mir.instructions.items(.data)[inst].payload;
360 const encoded_alignment = @ctz(mem_arg.alignment);87 const extra = mir.extraData(Mir.JumpTable, extra_index);
361 try leb128.writeUleb128(writer, encoded_alignment);88 const labels = mir.extra[extra.end..][0..extra.data.length];
362 try leb128.writeUleb128(writer, mem_arg.offset);89 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);
363}90 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));
91 // -1 because default label is not part of length/depth.
92 leb.writeUleb128(code.fixedWriter(), extra.data.length - 1) catch unreachable;
93 for (labels) |label| leb.writeUleb128(code.fixedWriter(), label) catch unreachable;
94
95 inst += 1;
96 continue :loop tags[inst];
97 },
36498
365fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {99 .call_nav => {
366 const wasm = emit.bin_file;100 try code.ensureUnusedCapacity(gpa, 6);
367 const comp = wasm.base.comp;101 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
368 const gpa = comp.gpa;102 if (is_obj) {
369 const label = emit.mir.instructions.items(.data)[inst].label;103 try wasm.out_relocs.append(gpa, .{
370 try emit.code.append(@intFromEnum(std.wasm.Opcode.call));104 .offset = @intCast(code.items.len),
371 const call_offset = emit.offset();105 .index = try wasm.navSymbolIndex(datas[inst].nav_index),
372 var buf: [5]u8 = undefined;106 .tag = .FUNCTION_INDEX_LEB,
373 leb128.writeUnsignedFixed(5, &buf, label);107 .addend = 0,
374 try emit.code.appendSlice(&buf);108 });
375109 code.appendNTimesAssumeCapacity(0, 5);
376 const zo = wasm.zig_object.?;110 } else {
377 try zo.relocs.append(gpa, .{111 const func_index = try wasm.navFunctionIndex(datas[inst].nav_index);
378 .offset = call_offset,112 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
379 .index = label,113 }
380 .tag = .FUNCTION_INDEX_LEB,114
381 });115 inst += 1;
382}116 continue :loop tags[inst];
117 },
383118
384fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {119 .call_indirect => {
385 const wasm = emit.bin_file;120 try code.ensureUnusedCapacity(gpa, 11);
386 const type_index = emit.mir.instructions.items(.data)[inst].label;121 const func_ty_index = datas[inst].func_ty;
387 try emit.code.append(@intFromEnum(std.wasm.Opcode.call_indirect));122 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));
388 // NOTE: If we remove unused function types in the future for incremental123 if (is_obj) {
389 // linking, we must also emit a relocation for this `type_index`124 try wasm.out_relocs.append(gpa, .{
390 const call_offset = emit.offset();125 .offset = @intCast(code.items.len),
391 var buf: [5]u8 = undefined;126 .index = func_ty_index,
392 leb128.writeUnsignedFixed(5, &buf, type_index);127 .tag = .TYPE_INDEX_LEB,
393 try emit.code.appendSlice(&buf);128 .addend = 0,
394129 });
395 const zo = wasm.zig_object.?;130 code.appendNTimesAssumeCapacity(0, 5);
396 try zo.relocs.append(wasm.base.comp.gpa, .{131 } else {
397 .offset = call_offset,132 leb.writeUleb128(code.fixedWriter(), @intFromEnum(func_ty_index)) catch unreachable;
398 .index = type_index,133 }
399 .tag = .TYPE_INDEX_LEB,134 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // table index
400 });135
401136 inst += 1;
402 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index137 continue :loop tags[inst];
403}138 },
404139
405fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {140 .global_set => {
406 const wasm = emit.bin_file;141 try code.ensureUnusedCapacity(gpa, 6);
407 const comp = wasm.base.comp;142 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
408 const gpa = comp.gpa;143 if (is_obj) {
409 const symbol_index = emit.mir.instructions.items(.data)[inst].label;144 try wasm.out_relocs.append(gpa, .{
410 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));145 .offset = @intCast(code.items.len),
411 const index_offset = emit.offset();146 .index = try wasm.stackPointerSymbolIndex(),
412 var buf: [5]u8 = undefined;147 .tag = .GLOBAL_INDEX_LEB,
413 leb128.writeUnsignedFixed(5, &buf, symbol_index);148 .addend = 0,
414 try emit.code.appendSlice(&buf);149 });
415150 code.appendNTimesAssumeCapacity(0, 5);
416 const zo = wasm.zig_object.?;151 } else {
417 try zo.relocs.append(gpa, .{152 const sp_global = try wasm.stackPointerGlobalIndex();
418 .offset = index_offset,153 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
419 .index = symbol_index,154 }
420 .tag = .TABLE_INDEX_SLEB,155
421 });156 inst += 1;
422}157 continue :loop tags[inst];
158 },
423159
424fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {160 .function_index => {
425 const wasm = emit.bin_file;161 try code.ensureUnusedCapacity(gpa, 6);
426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;162 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
427 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;163 if (is_obj) {
428 const mem_offset = emit.offset() + 1;164 try wasm.out_relocs.append(gpa, .{
429 const comp = wasm.base.comp;165 .offset = @intCast(code.items.len),
430 const gpa = comp.gpa;166 .index = try wasm.functionSymbolIndex(datas[inst].ip_index),
431 const target = comp.root_mod.resolved_target.result;167 .tag = .TABLE_INDEX_SLEB,
432 const is_wasm32 = target.cpu.arch == .wasm32;168 .addend = 0,
433 if (is_wasm32) {169 });
434 try emit.code.append(@intFromEnum(std.wasm.Opcode.i32_const));170 code.appendNTimesAssumeCapacity(0, 5);
435 var buf: [5]u8 = undefined;171 } else {
436 leb128.writeUnsignedFixed(5, &buf, mem.pointer);172 const func_index = try wasm.functionIndex(datas[inst].ip_index);
437 try emit.code.appendSlice(&buf);173 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
438 } else {174 }
439 try emit.code.append(@intFromEnum(std.wasm.Opcode.i64_const));175
440 var buf: [10]u8 = undefined;176 inst += 1;
441 leb128.writeUnsignedFixed(10, &buf, mem.pointer);177 continue :loop tags[inst];
442 try emit.code.appendSlice(&buf);178 },
443 }
444179
445 const zo = wasm.zig_object.?;180 .f32_const => {
446 try zo.relocs.append(gpa, .{181 try code.ensureUnusedCapacity(gpa, 5);
447 .offset = mem_offset,182 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));
448 .index = mem.pointer,183 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
449 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
450 .addend = @as(i32, @intCast(mem.offset)),
451 });
452}
453184
454fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {185 inst += 1;
455 const extra_index = emit.mir.instructions.items(.data)[inst].payload;186 continue :loop tags[inst];
456 const opcode = emit.mir.extra[extra_index];
457 const writer = emit.code.writer();
458 try emit.code.append(@intFromEnum(std.wasm.Opcode.misc_prefix));
459 try leb128.writeUleb128(writer, opcode);
460 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
461 // bulk-memory opcodes
462 .data_drop => {
463 const segment = emit.mir.extra[extra_index + 1];
464 try leb128.writeUleb128(writer, segment);
465 },187 },
466 .memory_init => {188
467 const segment = emit.mir.extra[extra_index + 1];189 .f64_const => {
468 try leb128.writeUleb128(writer, segment);190 try code.ensureUnusedCapacity(gpa, 9);
469 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const));
192 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
193 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);
194
195 inst += 1;
196 continue :loop tags[inst];
470 },197 },
471 .memory_fill => {198 .i32_const => {
472 try leb128.writeUleb128(writer, @as(u32, 0)); // memory index199 try code.ensureUnusedCapacity(gpa, 6);
200 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
201 leb.writeIleb128(code.fixedWriter(), datas[inst].imm32) catch unreachable;
202
203 inst += 1;
204 continue :loop tags[inst];
473 },205 },
474 .memory_copy => {206 .i64_const => {
475 try leb128.writeUleb128(writer, @as(u32, 0)); // dst memory index207 try code.ensureUnusedCapacity(gpa, 11);
476 try leb128.writeUleb128(writer, @as(u32, 0)); // src memory index208 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
209 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());
210 leb.writeIleb128(code.writer(), int64) catch unreachable;
211
212 inst += 1;
213 continue :loop tags[inst];
477 },214 },
478215
479 // nontrapping-float-to-int-conversion opcodes216 .i32_load,
480 .i32_trunc_sat_f32_s,217 .i64_load,
481 .i32_trunc_sat_f32_u,218 .f32_load,
482 .i32_trunc_sat_f64_s,219 .f64_load,
483 .i32_trunc_sat_f64_u,220 .i32_load8_s,
484 .i64_trunc_sat_f32_s,221 .i32_load8_u,
485 .i64_trunc_sat_f32_u,222 .i32_load16_s,
486 .i64_trunc_sat_f64_s,223 .i32_load16_u,
487 .i64_trunc_sat_f64_u,224 .i64_load8_s,
488 => {}, // opcode already written225 .i64_load8_u,
489 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),226 .i64_load16_s,
490 }227 .i64_load16_u,
491}228 .i64_load32_s,
492229 .i64_load32_u,
493fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {230 .i32_store,
494 const extra_index = emit.mir.instructions.items(.data)[inst].payload;231 .i64_store,
495 const opcode = emit.mir.extra[extra_index];232 .f32_store,
496 const writer = emit.code.writer();233 .f64_store,
497 try emit.code.append(@intFromEnum(std.wasm.Opcode.simd_prefix));234 .i32_store8,
498 try leb128.writeUleb128(writer, opcode);235 .i32_store16,
499 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {236 .i64_store8,
500 .v128_store,237 .i64_store16,
501 .v128_load,238 .i64_store32,
502 .v128_load8_splat,
503 .v128_load16_splat,
504 .v128_load32_splat,
505 .v128_load64_splat,
506 => {
507 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
508 try encodeMemArg(mem_arg, writer);
509 },
510 .v128_const,
511 .i8x16_shuffle,
512 => {239 => {
513 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];240 try code.ensureUnusedCapacity(gpa, 1 + 20);
514 try writer.writeAll(std.mem.asBytes(simd_value));241 code.appendAssumeCapacity(@intFromEnum(tags[inst]));
242 encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst]).data);
243 inst += 1;
244 continue :loop tags[inst];
515 },245 },
516 .i8x16_extract_lane_s,246
517 .i8x16_extract_lane_u,247 .end,
518 .i8x16_replace_lane,248 .@"return",
519 .i16x8_extract_lane_s,249 .@"unreachable",
520 .i16x8_extract_lane_u,250 .select,
521 .i16x8_replace_lane,251 .i32_eqz,
522 .i32x4_extract_lane,252 .i32_eq,
523 .i32x4_replace_lane,253 .i32_ne,
524 .i64x2_extract_lane,254 .i32_lt_s,
525 .i64x2_replace_lane,255 .i32_lt_u,
526 .f32x4_extract_lane,256 .i32_gt_s,
527 .f32x4_replace_lane,257 .i32_gt_u,
528 .f64x2_extract_lane,258 .i32_le_s,
529 .f64x2_replace_lane,259 .i32_le_u,
260 .i32_ge_s,
261 .i32_ge_u,
262 .i64_eqz,
263 .i64_eq,
264 .i64_ne,
265 .i64_lt_s,
266 .i64_lt_u,
267 .i64_gt_s,
268 .i64_gt_u,
269 .i64_le_s,
270 .i64_le_u,
271 .i64_ge_s,
272 .i64_ge_u,
273 .f32_eq,
274 .f32_ne,
275 .f32_lt,
276 .f32_gt,
277 .f32_le,
278 .f32_ge,
279 .f64_eq,
280 .f64_ne,
281 .f64_lt,
282 .f64_gt,
283 .f64_le,
284 .f64_ge,
285 .i32_add,
286 .i32_sub,
287 .i32_mul,
288 .i32_div_s,
289 .i32_div_u,
290 .i32_and,
291 .i32_or,
292 .i32_xor,
293 .i32_shl,
294 .i32_shr_s,
295 .i32_shr_u,
296 .i64_add,
297 .i64_sub,
298 .i64_mul,
299 .i64_div_s,
300 .i64_div_u,
301 .i64_and,
302 .i64_or,
303 .i64_xor,
304 .i64_shl,
305 .i64_shr_s,
306 .i64_shr_u,
307 .f32_abs,
308 .f32_neg,
309 .f32_ceil,
310 .f32_floor,
311 .f32_trunc,
312 .f32_nearest,
313 .f32_sqrt,
314 .f32_add,
315 .f32_sub,
316 .f32_mul,
317 .f32_div,
318 .f32_min,
319 .f32_max,
320 .f32_copysign,
321 .f64_abs,
322 .f64_neg,
323 .f64_ceil,
324 .f64_floor,
325 .f64_trunc,
326 .f64_nearest,
327 .f64_sqrt,
328 .f64_add,
329 .f64_sub,
330 .f64_mul,
331 .f64_div,
332 .f64_min,
333 .f64_max,
334 .f64_copysign,
335 .i32_wrap_i64,
336 .i64_extend_i32_s,
337 .i64_extend_i32_u,
338 .i32_extend8_s,
339 .i32_extend16_s,
340 .i64_extend8_s,
341 .i64_extend16_s,
342 .i64_extend32_s,
343 .f32_demote_f64,
344 .f64_promote_f32,
345 .i32_reinterpret_f32,
346 .i64_reinterpret_f64,
347 .f32_reinterpret_i32,
348 .f64_reinterpret_i64,
349 .i32_trunc_f32_s,
350 .i32_trunc_f32_u,
351 .i32_trunc_f64_s,
352 .i32_trunc_f64_u,
353 .i64_trunc_f32_s,
354 .i64_trunc_f32_u,
355 .i64_trunc_f64_s,
356 .i64_trunc_f64_u,
357 .f32_convert_i32_s,
358 .f32_convert_i32_u,
359 .f32_convert_i64_s,
360 .f32_convert_i64_u,
361 .f64_convert_i32_s,
362 .f64_convert_i32_u,
363 .f64_convert_i64_s,
364 .f64_convert_i64_u,
365 .i32_rem_s,
366 .i32_rem_u,
367 .i64_rem_s,
368 .i64_rem_u,
369 .i32_popcnt,
370 .i64_popcnt,
371 .i32_clz,
372 .i32_ctz,
373 .i64_clz,
374 .i64_ctz,
530 => {375 => {
531 try writer.writeByte(@as(u8, @intCast(emit.mir.extra[extra_index + 1])));376 try code.append(gpa, @intFromEnum(tags[inst]));
377 inst += 1;
378 continue :loop tags[inst];
532 },379 },
533 .i8x16_splat,
534 .i16x8_splat,
535 .i32x4_splat,
536 .i64x2_splat,
537 .f32x4_splat,
538 .f64x2_splat,
539 => {}, // opcode already written
540 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
541 }
542}
543380
544fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {381 .misc_prefix => {
545 const extra_index = emit.mir.instructions.items(.data)[inst].payload;382 try code.ensureUnusedCapacity(gpa, 6 + 6);
546 const opcode = emit.mir.extra[extra_index];383 const extra_index = datas[inst].payload;
547 const writer = emit.code.writer();384 const opcode = mir.extra[extra_index];
548 try emit.code.append(@intFromEnum(std.wasm.Opcode.atomics_prefix));385 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));
549 try leb128.writeUleb128(writer, opcode);386 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
550 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {387 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {
551 .i32_atomic_load,388 // bulk-memory opcodes
552 .i64_atomic_load,389 .data_drop => {
553 .i32_atomic_load8_u,390 const segment = mir.extra[extra_index + 1];
554 .i32_atomic_load16_u,391 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
555 .i64_atomic_load8_u,392
556 .i64_atomic_load16_u,393 inst += 1;
557 .i64_atomic_load32_u,394 continue :loop tags[inst];
558 .i32_atomic_store,395 },
559 .i64_atomic_store,396 .memory_init => {
560 .i32_atomic_store8,397 const segment = mir.extra[extra_index + 1];
561 .i32_atomic_store16,398 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;
562 .i64_atomic_store8,399 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
563 .i64_atomic_store16,400
564 .i64_atomic_store32,401 inst += 1;
565 .i32_atomic_rmw_add,402 continue :loop tags[inst];
566 .i64_atomic_rmw_add,403 },
567 .i32_atomic_rmw8_add_u,404 .memory_fill => {
568 .i32_atomic_rmw16_add_u,405 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index
569 .i64_atomic_rmw8_add_u,406
570 .i64_atomic_rmw16_add_u,407 inst += 1;
571 .i64_atomic_rmw32_add_u,408 continue :loop tags[inst];
572 .i32_atomic_rmw_sub,409 },
573 .i64_atomic_rmw_sub,410 .memory_copy => {
574 .i32_atomic_rmw8_sub_u,411 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index
575 .i32_atomic_rmw16_sub_u,412 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index
576 .i64_atomic_rmw8_sub_u,413
577 .i64_atomic_rmw16_sub_u,414 inst += 1;
578 .i64_atomic_rmw32_sub_u,415 continue :loop tags[inst];
579 .i32_atomic_rmw_and,416 },
580 .i64_atomic_rmw_and,417
581 .i32_atomic_rmw8_and_u,418 // nontrapping-float-to-int-conversion opcodes
582 .i32_atomic_rmw16_and_u,419 .i32_trunc_sat_f32_s,
583 .i64_atomic_rmw8_and_u,420 .i32_trunc_sat_f32_u,
584 .i64_atomic_rmw16_and_u,421 .i32_trunc_sat_f64_s,
585 .i64_atomic_rmw32_and_u,422 .i32_trunc_sat_f64_u,
586 .i32_atomic_rmw_or,423 .i64_trunc_sat_f32_s,
587 .i64_atomic_rmw_or,424 .i64_trunc_sat_f32_u,
588 .i32_atomic_rmw8_or_u,425 .i64_trunc_sat_f64_s,
589 .i32_atomic_rmw16_or_u,426 .i64_trunc_sat_f64_u,
590 .i64_atomic_rmw8_or_u,427 => {
591 .i64_atomic_rmw16_or_u,428 inst += 1;
592 .i64_atomic_rmw32_or_u,429 continue :loop tags[inst];
593 .i32_atomic_rmw_xor,430 },
594 .i64_atomic_rmw_xor,431
595 .i32_atomic_rmw8_xor_u,432 _ => unreachable,
596 .i32_atomic_rmw16_xor_u,433 }
597 .i64_atomic_rmw8_xor_u,434 unreachable;
598 .i64_atomic_rmw16_xor_u,435 },
599 .i64_atomic_rmw32_xor_u,436 .simd_prefix => {
600 .i32_atomic_rmw_xchg,437 try code.ensureUnusedCapacity(gpa, 6 + 20);
601 .i64_atomic_rmw_xchg,438 const extra_index = mir.instructions.items(.data)[inst].payload;
602 .i32_atomic_rmw8_xchg_u,439 const opcode = mir.extra[extra_index];
603 .i32_atomic_rmw16_xchg_u,440 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));
604 .i64_atomic_rmw8_xchg_u,441 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
605 .i64_atomic_rmw16_xchg_u,442 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {
606 .i64_atomic_rmw32_xchg_u,443 .v128_store,
607444 .v128_load,
608 .i32_atomic_rmw_cmpxchg,445 .v128_load8_splat,
609 .i64_atomic_rmw_cmpxchg,446 .v128_load16_splat,
610 .i32_atomic_rmw8_cmpxchg_u,447 .v128_load32_splat,
611 .i32_atomic_rmw16_cmpxchg_u,448 .v128_load64_splat,
612 .i64_atomic_rmw8_cmpxchg_u,449 => {
613 .i64_atomic_rmw16_cmpxchg_u,450 encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data);
614 .i64_atomic_rmw32_cmpxchg_u,451 inst += 1;
615 => {452 continue :loop tags[inst];
616 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;453 },
617 try encodeMemArg(mem_arg, writer);454 .v128_const, .i8x16_shuffle => {
455 code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));
456 inst += 1;
457 continue :loop tags[inst];
458 },
459 .i8x16_extract_lane_s,
460 .i8x16_extract_lane_u,
461 .i8x16_replace_lane,
462 .i16x8_extract_lane_s,
463 .i16x8_extract_lane_u,
464 .i16x8_replace_lane,
465 .i32x4_extract_lane,
466 .i32x4_replace_lane,
467 .i64x2_extract_lane,
468 .i64x2_replace_lane,
469 .f32x4_extract_lane,
470 .f32x4_replace_lane,
471 .f64x2_extract_lane,
472 .f64x2_replace_lane,
473 => {
474 code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1]));
475 inst += 1;
476 continue :loop tags[inst];
477 },
478 .i8x16_splat,
479 .i16x8_splat,
480 .i32x4_splat,
481 .i64x2_splat,
482 .f32x4_splat,
483 .f64x2_splat,
484 => {
485 inst += 1;
486 continue :loop tags[inst];
487 },
488 _ => unreachable,
489 }
490 unreachable;
618 },491 },
619 .atomic_fence => {492 .atomics_prefix => {
620 // TODO: When multi-memory proposal is accepted and implemented in the compiler,493 try code.ensureUnusedCapacity(gpa, 6 + 20);
621 // change this to (user-)specified index, rather than hardcode it to memory index 0.494
622 const memory_index: u32 = 0;495 const extra_index = mir.instructions.items(.data)[inst].payload;
623 try leb128.writeUleb128(writer, memory_index);496 const opcode = mir.extra[extra_index];
497 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
498 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
499 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {
500 .i32_atomic_load,
501 .i64_atomic_load,
502 .i32_atomic_load8_u,
503 .i32_atomic_load16_u,
504 .i64_atomic_load8_u,
505 .i64_atomic_load16_u,
506 .i64_atomic_load32_u,
507 .i32_atomic_store,
508 .i64_atomic_store,
509 .i32_atomic_store8,
510 .i32_atomic_store16,
511 .i64_atomic_store8,
512 .i64_atomic_store16,
513 .i64_atomic_store32,
514 .i32_atomic_rmw_add,
515 .i64_atomic_rmw_add,
516 .i32_atomic_rmw8_add_u,
517 .i32_atomic_rmw16_add_u,
518 .i64_atomic_rmw8_add_u,
519 .i64_atomic_rmw16_add_u,
520 .i64_atomic_rmw32_add_u,
521 .i32_atomic_rmw_sub,
522 .i64_atomic_rmw_sub,
523 .i32_atomic_rmw8_sub_u,
524 .i32_atomic_rmw16_sub_u,
525 .i64_atomic_rmw8_sub_u,
526 .i64_atomic_rmw16_sub_u,
527 .i64_atomic_rmw32_sub_u,
528 .i32_atomic_rmw_and,
529 .i64_atomic_rmw_and,
530 .i32_atomic_rmw8_and_u,
531 .i32_atomic_rmw16_and_u,
532 .i64_atomic_rmw8_and_u,
533 .i64_atomic_rmw16_and_u,
534 .i64_atomic_rmw32_and_u,
535 .i32_atomic_rmw_or,
536 .i64_atomic_rmw_or,
537 .i32_atomic_rmw8_or_u,
538 .i32_atomic_rmw16_or_u,
539 .i64_atomic_rmw8_or_u,
540 .i64_atomic_rmw16_or_u,
541 .i64_atomic_rmw32_or_u,
542 .i32_atomic_rmw_xor,
543 .i64_atomic_rmw_xor,
544 .i32_atomic_rmw8_xor_u,
545 .i32_atomic_rmw16_xor_u,
546 .i64_atomic_rmw8_xor_u,
547 .i64_atomic_rmw16_xor_u,
548 .i64_atomic_rmw32_xor_u,
549 .i32_atomic_rmw_xchg,
550 .i64_atomic_rmw_xchg,
551 .i32_atomic_rmw8_xchg_u,
552 .i32_atomic_rmw16_xchg_u,
553 .i64_atomic_rmw8_xchg_u,
554 .i64_atomic_rmw16_xchg_u,
555 .i64_atomic_rmw32_xchg_u,
556
557 .i32_atomic_rmw_cmpxchg,
558 .i64_atomic_rmw_cmpxchg,
559 .i32_atomic_rmw8_cmpxchg_u,
560 .i32_atomic_rmw16_cmpxchg_u,
561 .i64_atomic_rmw8_cmpxchg_u,
562 .i64_atomic_rmw16_cmpxchg_u,
563 .i64_atomic_rmw32_cmpxchg_u,
564 => {
565 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;
566 encodeMemArg(code, mem_arg);
567 inst += 1;
568 continue :loop tags[inst];
569 },
570 .atomic_fence => {
571 // Hard-codes memory index 0 since multi-memory proposal is
572 // not yet accepted nor implemented.
573 const memory_index: u32 = 0;
574 leb.writeUleb128(code.fixedWriter(), memory_index) catch unreachable;
575 inst += 1;
576 continue :loop tags[inst];
577 },
578 }
579 unreachable;
624 },580 },
625 else => |tag| return emit.fail("TODO: Implement atomic instruction: {s}", .{@tagName(tag)}),
626 }581 }
582 unreachable;
627}583}
628584
629fn emitMemFill(emit: *Emit) !void {585/// Assert 20 unused capacity.
630 try emit.code.append(0xFC);586fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
631 try emit.code.append(0x0B);587 assert(code.unusedCapacitySlice().len >= 20);
632 // When multi-memory proposal reaches phase 4, we588 // Wasm encodes alignment as power of 2, rather than natural alignment.
633 // can emit a different memory index here.589 const encoded_alignment = @ctz(mem_arg.alignment);
634 // For now we will always emit index 0.590 leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable;
635 try leb128.writeUleb128(emit.code.writer(), @as(u32, 0));591 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
636}
637
638fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
639 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
640 const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data;
641 try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column);
642}
643
644fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
645 if (emit.dbg_output != .dwarf) return;
646
647 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
648 const delta_pc = emit.offset() - emit.prev_di_offset;
649 // TODO: This must emit a relocation to calculate the offset relative
650 // to the code section start.
651 try emit.dbg_output.dwarf.advancePCAndLine(delta_line, delta_pc);
652
653 emit.prev_di_line = line;
654 emit.prev_di_column = column;
655 emit.prev_di_offset = emit.offset();
656}
657
658fn emitDbgPrologueEnd(emit: *Emit) !void {
659 if (emit.dbg_output != .dwarf) return;
660
661 try emit.dbg_output.dwarf.setPrologueEnd();
662 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
663}592}
664593
665fn emitDbgEpilogueBegin(emit: *Emit) !void {594fn uavRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOff) !void {
666 if (emit.dbg_output != .dwarf) return;595 const comp = wasm.base.comp;
596 const gpa = comp.gpa;
597 const target = comp.root_mod.resolved_target.result;
598 const is_wasm32 = target.cpu.arch == .wasm32;
599 const is_obj = comp.config.output_mode == .Obj;
600 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
601
602 try code.ensureUnusedCapacity(gpa, 11);
603 code.appendAssumeCapacity(@intFromEnum(opcode));
604
605 // If outputting an object file, this needs to be a relocation, since global
606 // constant data may be mixed with other object files in the final link.
607 if (is_obj) {
608 try wasm.out_relocs.append(gpa, .{
609 .offset = @intCast(code.items.len),
610 .index = try wasm.uavSymbolIndex(data.ip_index),
611 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
612 .addend = data.offset,
613 });
614 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
615 return;
616 }
667617
668 try emit.dbg_output.dwarf.setEpilogueBegin();618 // When linking into the final binary, no relocation mechanism is necessary.
669 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);619 const addr: i64 = try wasm.uavAddr(data.ip_index);
620 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
670}621}
src/arch/wasm/Mir.zig+69-83
...@@ -10,10 +10,12 @@ const Mir = @This();...@@ -10,10 +10,12 @@ const Mir = @This();
10const InternPool = @import("../../InternPool.zig");10const InternPool = @import("../../InternPool.zig");
11const Wasm = @import("../../link/Wasm.zig");11const Wasm = @import("../../link/Wasm.zig");
1212
13const builtin = @import("builtin");
13const std = @import("std");14const std = @import("std");
15const assert = std.debug.assert;
1416
15/// A struct of array that represents each individual wasm17instruction_tags: []const Inst.Tag,
16instructions: std.MultiArrayList(Inst).Slice,18instruction_datas: []const Inst.Data,
17/// A slice of indexes where the meaning of the data is determined by the19/// A slice of indexes where the meaning of the data is determined by the
18/// `Inst.Tag` value.20/// `Inst.Tag` value.
19extra: []const u32,21extra: []const u32,
...@@ -28,13 +30,7 @@ pub const Inst = struct {...@@ -28,13 +30,7 @@ pub const Inst = struct {
28 /// The position of a given MIR isntruction with the instruction list.30 /// The position of a given MIR isntruction with the instruction list.
29 pub const Index = u32;31 pub const Index = u32;
3032
31 /// Contains all possible wasm opcodes the Zig compiler may emit33 /// Some tags match wasm opcode values to facilitate trivial lowering.
32 /// Rather than re-using std.wasm.Opcode, we only declare the opcodes
33 /// we need, and also use this possibility to document how to access
34 /// their payload.
35 ///
36 /// Note: Uses its actual opcode value representation to easily convert
37 /// to and from its binary representation.
38 pub const Tag = enum(u8) {34 pub const Tag = enum(u8) {
39 /// Uses `nop`35 /// Uses `nop`
40 @"unreachable" = 0x00,36 @"unreachable" = 0x00,
...@@ -46,19 +42,27 @@ pub const Inst = struct {...@@ -46,19 +42,27 @@ pub const Inst = struct {
46 ///42 ///
47 /// Type of the loop is given in data `block_type`43 /// Type of the loop is given in data `block_type`
48 loop = 0x03,44 loop = 0x03,
45 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
46 /// memory address of an unnamed constant. When emitting an object
47 /// file, this adds a relocation.
48 ///
49 /// Data is `ip_index`.
50 uav_ref,
51 /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
52 /// memory address of an unnamed constant, offset by an integer value.
53 /// When emitting an object file, this adds a relocation.
54 ///
55 /// Data is `payload` pointing to a `UavRefOff`.
56 uav_ref_off,
49 /// Inserts debug information about the current line and column57 /// Inserts debug information about the current line and column
50 /// of the source code58 /// of the source code
51 ///59 ///
52 /// Uses `payload` of which the payload type is `DbgLineColumn`60 /// Uses `payload` of which the payload type is `DbgLineColumn`
53 dbg_line = 0x06,61 dbg_line = 0x06,
54 /// Emits epilogue begin debug information62 /// Emits epilogue begin debug information. Marks the end of the function.
55 ///63 ///
56 /// Uses `nop`64 /// Uses `nop`
57 dbg_epilogue_begin = 0x07,65 dbg_epilogue_begin = 0x07,
58 /// Emits prologue end debug information
59 ///
60 /// Uses `nop`
61 dbg_prologue_end = 0x08,
62 /// Represents the end of a function body or an initialization expression66 /// Represents the end of a function body or an initialization expression
63 ///67 ///
64 /// Payload is `nop`68 /// Payload is `nop`
...@@ -80,13 +84,13 @@ pub const Inst = struct {...@@ -80,13 +84,13 @@ pub const Inst = struct {
80 ///84 ///
81 /// Uses `nop`85 /// Uses `nop`
82 @"return" = 0x0F,86 @"return" = 0x0F,
87 /// Calls a function using `nav_index`.
88 call_nav,
83 /// Calls a function pointer by its function signature89 /// Calls a function pointer by its function signature
84 /// and index into the function table.90 /// and index into the function table.
85 ///91 ///
86 /// Uses `label`92 /// Uses `func_ty`
87 call_indirect = 0x11,93 call_indirect = 0x11,
88 /// Calls a function using `nav_index`.
89 call_nav,
90 /// Calls a function using `func_index`.94 /// Calls a function using `func_index`.
91 call_func,95 call_func,
92 /// Calls a function by its index.96 /// Calls a function by its index.
...@@ -94,9 +98,11 @@ pub const Inst = struct {...@@ -94,9 +98,11 @@ pub const Inst = struct {
94 /// The function is the auto-generated tag name function for the type98 /// The function is the auto-generated tag name function for the type
95 /// provided in `ip_index`.99 /// provided in `ip_index`.
96 call_tag_name,100 call_tag_name,
97 /// Contains a symbol to a function pointer101 /// Lowers to an i32_const containing the index of a function.
98 /// uses `label`102 /// When emitting an object file, this adds a relocation.
103 /// Uses `ip_index`.
99 function_index,104 function_index,
105
100 /// Pops three values from the stack and pushes106 /// Pops three values from the stack and pushes
101 /// the first or second value dependent on the third value.107 /// the first or second value dependent on the third value.
102 /// Uses `tag`108 /// Uses `tag`
...@@ -115,15 +121,11 @@ pub const Inst = struct {...@@ -115,15 +121,11 @@ pub const Inst = struct {
115 ///121 ///
116 /// Uses `label`122 /// Uses `label`
117 local_tee = 0x22,123 local_tee = 0x22,
118 /// Loads a (mutable) global at given index onto the stack124 /// Pops a value from the stack and sets the stack pointer global.
125 /// The value must be the same type as the stack pointer global.
119 ///126 ///
120 /// Uses `label`127 /// Uses `tag` (no additional data).
121 global_get = 0x23,128 global_set_sp,
122 /// Pops a value from the stack and sets the global at given index.
123 /// Note: Both types must be equal and global must be marked mutable.
124 ///
125 /// Uses `label`.
126 global_set = 0x24,
127 /// Loads a 32-bit integer from memory (data section) onto the stack129 /// Loads a 32-bit integer from memory (data section) onto the stack
128 /// Pops the value from the stack which represents the offset into memory.130 /// Pops the value from the stack which represents the offset into memory.
129 ///131 ///
...@@ -259,19 +261,19 @@ pub const Inst = struct {...@@ -259,19 +261,19 @@ pub const Inst = struct {
259 /// Loads a 32-bit signed immediate value onto the stack261 /// Loads a 32-bit signed immediate value onto the stack
260 ///262 ///
261 /// Uses `imm32`263 /// Uses `imm32`
262 i32_const = 0x41,264 i32_const,
263 /// Loads a i64-bit signed immediate value onto the stack265 /// Loads a i64-bit signed immediate value onto the stack
264 ///266 ///
265 /// uses `payload` of type `Imm64`267 /// uses `payload` of type `Imm64`
266 i64_const = 0x42,268 i64_const,
267 /// Loads a 32-bit float value onto the stack.269 /// Loads a 32-bit float value onto the stack.
268 ///270 ///
269 /// Uses `float32`271 /// Uses `float32`
270 f32_const = 0x43,272 f32_const,
271 /// Loads a 64-bit float value onto the stack.273 /// Loads a 64-bit float value onto the stack.
272 ///274 ///
273 /// Uses `payload` of type `Float64`275 /// Uses `payload` of type `Float64`
274 f64_const = 0x44,276 f64_const,
275 /// Uses `tag`277 /// Uses `tag`
276 i32_eqz = 0x45,278 i32_eqz = 0x45,
277 /// Uses `tag`279 /// Uses `tag`
...@@ -525,25 +527,19 @@ pub const Inst = struct {...@@ -525,25 +527,19 @@ pub const Inst = struct {
525 ///527 ///
526 /// The `data` field depends on the extension instruction and528 /// The `data` field depends on the extension instruction and
527 /// may contain additional data.529 /// may contain additional data.
528 misc_prefix = 0xFC,530 misc_prefix,
529 /// The instruction consists of a simd opcode.531 /// The instruction consists of a simd opcode.
530 /// The actual simd-opcode is found at payload's index.532 /// The actual simd-opcode is found at payload's index.
531 ///533 ///
532 /// The `data` field depends on the simd instruction and534 /// The `data` field depends on the simd instruction and
533 /// may contain additional data.535 /// may contain additional data.
534 simd_prefix = 0xFD,536 simd_prefix,
535 /// The instruction consists of an atomics opcode.537 /// The instruction consists of an atomics opcode.
536 /// The actual atomics-opcode is found at payload's index.538 /// The actual atomics-opcode is found at payload's index.
537 ///539 ///
538 /// The `data` field depends on the atomics instruction and540 /// The `data` field depends on the atomics instruction and
539 /// may contain additional data.541 /// may contain additional data.
540 atomics_prefix = 0xFE,542 atomics_prefix = 0xFE,
541 /// Contains a symbol to a memory address
542 /// Uses `label`
543 ///
544 /// Note: This uses `0xFF` as value as it is unused and not reserved
545 /// by the wasm specification, making it safe to use.
546 memory_address = 0xFF,
547543
548 /// From a given wasm opcode, returns a MIR tag.544 /// From a given wasm opcode, returns a MIR tag.
549 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {545 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {
...@@ -563,30 +559,38 @@ pub const Inst = struct {...@@ -563,30 +559,38 @@ pub const Inst = struct {
563 /// Uses no additional data559 /// Uses no additional data
564 tag: void,560 tag: void,
565 /// Contains the result type of a block561 /// Contains the result type of a block
566 ///
567 /// Used by `block` and `loop`
568 block_type: u8,562 block_type: u8,
569 /// Contains an u32 index into a wasm section entry, such as a local.563 /// Label: Each structured control instruction introduces an implicit label.
570 /// Note: This is not an index to another instruction.564 /// Labels are targets for branch instructions that reference them with
571 ///565 /// label indices. Unlike with other index spaces, indexing of labels
572 /// Used by e.g. `local_get`, `local_set`, etc.566 /// is relative by nesting depth, that is, label 0 refers to the
567 /// innermost structured control instruction enclosing the referring
568 /// branch instruction, while increasing indices refer to those farther
569 /// out. Consequently, labels can only be referenced from within the
570 /// associated structured control instruction.
573 label: u32,571 label: u32,
572 /// Local: The index space for locals is only accessible inside a function and
573 /// includes the parameters of that function, which precede the local
574 /// variables.
575 local: u32,
574 /// A 32-bit immediate value.576 /// A 32-bit immediate value.
575 ///
576 /// Used by `i32_const`
577 imm32: i32,577 imm32: i32,
578 /// A 32-bit float value578 /// A 32-bit float value
579 ///
580 /// Used by `f32_float`
581 float32: f32,579 float32: f32,
582 /// Index into `extra`. Meaning of what can be found there is context-dependent.580 /// Index into `extra`. Meaning of what can be found there is context-dependent.
583 ///
584 /// Used by e.g. `br_table`
585 payload: u32,581 payload: u32,
586582
587 ip_index: InternPool.Index,583 ip_index: InternPool.Index,
588 nav_index: InternPool.Nav.Index,584 nav_index: InternPool.Nav.Index,
589 func_index: Wasm.FunctionIndex,585 func_index: Wasm.FunctionIndex,
586 func_ty: Wasm.FunctionType.Index,
587
588 comptime {
589 switch (builtin.mode) {
590 .Debug, .ReleaseSafe => {},
591 .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4),
592 }
593 }
590 };594 };
591};595};
592596
...@@ -616,28 +620,19 @@ pub const JumpTable = struct {...@@ -616,28 +620,19 @@ pub const JumpTable = struct {
616 length: u32,620 length: u32,
617};621};
618622
619/// Stores an unsigned 64bit integer
620/// into a 32bit most significant bits field
621/// and a 32bit least significant bits field.
622///
623/// This uses an unsigned integer rather than a signed integer
624/// as we can easily store those into `extra`
625pub const Imm64 = struct {623pub const Imm64 = struct {
626 msb: u32,624 msb: u32,
627 lsb: u32,625 lsb: u32,
628626
629 pub fn fromU64(imm: u64) Imm64 {627 pub fn init(full: u64) Imm64 {
630 return .{628 return .{
631 .msb = @as(u32, @truncate(imm >> 32)),629 .msb = @truncate(full >> 32),
632 .lsb = @as(u32, @truncate(imm)),630 .lsb = @truncate(full),
633 };631 };
634 }632 }
635633
636 pub fn toU64(self: Imm64) u64 {634 pub fn toInt(i: Imm64) u64 {
637 var result: u64 = 0;635 return (@as(u64, i.msb) << 32) | @as(u64, i.lsb);
638 result |= @as(u64, self.msb) << 32;
639 result |= @as(u64, self.lsb);
640 return result;
641 }636 }
642};637};
643638
...@@ -645,23 +640,16 @@ pub const Float64 = struct {...@@ -645,23 +640,16 @@ pub const Float64 = struct {
645 msb: u32,640 msb: u32,
646 lsb: u32,641 lsb: u32,
647642
648 pub fn fromFloat64(float: f64) Float64 {643 pub fn init(f: f64) Float64 {
649 const tmp = @as(u64, @bitCast(float));644 const int: u64 = @bitCast(f);
650 return .{645 return .{
651 .msb = @as(u32, @truncate(tmp >> 32)),646 .msb = @truncate(int >> 32),
652 .lsb = @as(u32, @truncate(tmp)),647 .lsb = @truncate(int),
653 };648 };
654 }649 }
655650
656 pub fn toF64(self: Float64) f64 {651 pub fn toInt(f: Float64) u64 {
657 @as(f64, @bitCast(self.toU64()));652 return (@as(u64, f.msb) << 32) | @as(u64, f.lsb);
658 }
659
660 pub fn toU64(self: Float64) u64 {
661 var result: u64 = 0;
662 result |= @as(u64, self.msb) << 32;
663 result |= @as(u64, self.lsb);
664 return result;
665 }653 }
666};654};
667655
...@@ -670,11 +658,9 @@ pub const MemArg = struct {...@@ -670,11 +658,9 @@ pub const MemArg = struct {
670 alignment: u32,658 alignment: u32,
671};659};
672660
673/// Represents a memory address, which holds both the pointer661pub const UavRefOff = struct {
674/// or the parent pointer and the offset to it.662 ip_index: InternPool.Index,
675pub const Memory = struct {663 offset: i32,
676 pointer: u32,
677 offset: u32,
678};664};
679665
680/// Maps a source line with wasm bytecode666/// Maps a source line with wasm bytecode
src/arch/x86_64/CodeGen.zig+1-1
...@@ -1040,7 +1040,7 @@ pub fn generateLazy(...@@ -1040,7 +1040,7 @@ pub fn generateLazy(
1040 emit.emitMir() catch |err| switch (err) {1040 emit.emitMir() catch |err| switch (err) {
1041 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),1041 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?),
1042 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}),1042 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}),
1043 error.CannotEncode => return function.fail("failed to find encode x86 instruction (Zig compiler bug)", .{}),1043 error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}),
1044 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}),1044 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}),
1045 };1045 };
1046}1046}
src/codegen/llvm.zig+5-5
...@@ -1838,11 +1838,11 @@ pub const Object = struct {...@@ -1838,11 +1838,11 @@ pub const Object = struct {
1838 o: *Object,1838 o: *Object,
1839 zcu: *Zcu,1839 zcu: *Zcu,
1840 exported_value: InternPool.Index,1840 exported_value: InternPool.Index,
1841 export_indices: []const u32,1841 export_indices: []const Zcu.Export.Index,
1842 ) link.File.UpdateExportsError!void {1842 ) link.File.UpdateExportsError!void {
1843 const gpa = zcu.gpa;1843 const gpa = zcu.gpa;
1844 const ip = &zcu.intern_pool;1844 const ip = &zcu.intern_pool;
1845 const main_exp_name = try o.builder.strtabString(zcu.all_exports.items[export_indices[0]].opts.name.toSlice(ip));1845 const main_exp_name = try o.builder.strtabString(export_indices[0].ptr(zcu).opts.name.toSlice(ip));
1846 const global_index = i: {1846 const global_index = i: {
1847 const gop = try o.uav_map.getOrPut(gpa, exported_value);1847 const gop = try o.uav_map.getOrPut(gpa, exported_value);
1848 if (gop.found_existing) {1848 if (gop.found_existing) {
...@@ -1873,11 +1873,11 @@ pub const Object = struct {...@@ -1873,11 +1873,11 @@ pub const Object = struct {
1873 o: *Object,1873 o: *Object,
1874 zcu: *Zcu,1874 zcu: *Zcu,
1875 global_index: Builder.Global.Index,1875 global_index: Builder.Global.Index,
1876 export_indices: []const u32,1876 export_indices: []const Zcu.Export.Index,
1877 ) link.File.UpdateExportsError!void {1877 ) link.File.UpdateExportsError!void {
1878 const comp = zcu.comp;1878 const comp = zcu.comp;
1879 const ip = &zcu.intern_pool;1879 const ip = &zcu.intern_pool;
1880 const first_export = zcu.all_exports.items[export_indices[0]];1880 const first_export = export_indices[0].ptr(zcu);
18811881
1882 // We will rename this global to have a name matching `first_export`.1882 // We will rename this global to have a name matching `first_export`.
1883 // Successive exports become aliases.1883 // Successive exports become aliases.
...@@ -1934,7 +1934,7 @@ pub const Object = struct {...@@ -1934,7 +1934,7 @@ pub const Object = struct {
1934 // Until then we iterate over existing aliases and make them point1934 // Until then we iterate over existing aliases and make them point
1935 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.1935 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
1936 for (export_indices[1..]) |export_idx| {1936 for (export_indices[1..]) |export_idx| {
1937 const exp = zcu.all_exports.items[export_idx];1937 const exp = export_idx.ptr(zcu);
1938 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));1938 const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip));
1939 if (o.builder.getGlobal(exp_name)) |global| {1939 if (o.builder.getGlobal(exp_name)) |global| {
1940 switch (global.ptrConst(&o.builder).kind) {1940 switch (global.ptrConst(&o.builder).kind) {
src/link/C.zig+1-1
...@@ -469,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -469,7 +469,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
469 defer export_names.deinit(gpa);469 defer export_names.deinit(gpa);
470 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));470 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
471 for (zcu.single_exports.values()) |export_index| {471 for (zcu.single_exports.values()) |export_index| {
472 export_names.putAssumeCapacity(zcu.all_exports.items[export_index].opts.name, {});472 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
473 }473 }
474 for (zcu.multi_exports.values()) |info| {474 for (zcu.multi_exports.values()) |info| {
475 try export_names.ensureUnusedCapacity(gpa, info.len);475 try export_names.ensureUnusedCapacity(gpa, info.len);
src/link/Coff.zig+4-4
...@@ -1478,7 +1478,7 @@ pub fn updateExports(...@@ -1478,7 +1478,7 @@ pub fn updateExports(
1478 coff: *Coff,1478 coff: *Coff,
1479 pt: Zcu.PerThread,1479 pt: Zcu.PerThread,
1480 exported: Zcu.Exported,1480 exported: Zcu.Exported,
1481 export_indices: []const u32,1481 export_indices: []const Zcu.Export.Index,
1482) link.File.UpdateExportsError!void {1482) link.File.UpdateExportsError!void {
1483 if (build_options.skip_non_native and builtin.object_format != .coff) {1483 if (build_options.skip_non_native and builtin.object_format != .coff) {
1484 @panic("Attempted to compile for object format that was disabled by build configuration");1484 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -1493,7 +1493,7 @@ pub fn updateExports(...@@ -1493,7 +1493,7 @@ pub fn updateExports(
1493 // Even in the case of LLVM, we need to notice certain exported symbols in order to1493 // Even in the case of LLVM, we need to notice certain exported symbols in order to
1494 // detect the default subsystem.1494 // detect the default subsystem.
1495 for (export_indices) |export_idx| {1495 for (export_indices) |export_idx| {
1496 const exp = zcu.all_exports.items[export_idx];1496 const exp = export_idx.ptr(zcu);
1497 const exported_nav_index = switch (exp.exported) {1497 const exported_nav_index = switch (exp.exported) {
1498 .nav => |nav| nav,1498 .nav => |nav| nav,
1499 .uav => continue,1499 .uav => continue,
...@@ -1536,7 +1536,7 @@ pub fn updateExports(...@@ -1536,7 +1536,7 @@ pub fn updateExports(
1536 break :blk coff.navs.getPtr(nav).?;1536 break :blk coff.navs.getPtr(nav).?;
1537 },1537 },
1538 .uav => |uav| coff.uavs.getPtr(uav) orelse blk: {1538 .uav => |uav| coff.uavs.getPtr(uav) orelse blk: {
1539 const first_exp = zcu.all_exports.items[export_indices[0]];1539 const first_exp = export_indices[0].ptr(zcu);
1540 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);1540 const res = try coff.lowerUav(pt, uav, .none, first_exp.src);
1541 switch (res) {1541 switch (res) {
1542 .mcv => {},1542 .mcv => {},
...@@ -1555,7 +1555,7 @@ pub fn updateExports(...@@ -1555,7 +1555,7 @@ pub fn updateExports(
1555 const atom = coff.getAtom(atom_index);1555 const atom = coff.getAtom(atom_index);
15561556
1557 for (export_indices) |export_idx| {1557 for (export_indices) |export_idx| {
1558 const exp = zcu.all_exports.items[export_idx];1558 const exp = export_idx.ptr(zcu);
1559 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});1559 log.debug("adding new export '{}'", .{exp.opts.name.fmt(&zcu.intern_pool)});
15601560
1561 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {1561 if (exp.opts.section.toSlice(&zcu.intern_pool)) |section_name| {
src/link/Elf/ZigObject.zig+2-2
...@@ -1758,7 +1758,7 @@ pub fn updateExports(...@@ -1758,7 +1758,7 @@ pub fn updateExports(
1758 break :blk self.navs.getPtr(nav).?;1758 break :blk self.navs.getPtr(nav).?;
1759 },1759 },
1760 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1760 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1761 const first_exp = zcu.all_exports.items[export_indices[0]];1761 const first_exp = export_indices[0].ptr(zcu);
1762 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);1762 const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src);
1763 switch (res) {1763 switch (res) {
1764 .mcv => {},1764 .mcv => {},
...@@ -1779,7 +1779,7 @@ pub fn updateExports(...@@ -1779,7 +1779,7 @@ pub fn updateExports(
1779 const esym_shndx = self.symtab.items(.shndx)[esym_index];1779 const esym_shndx = self.symtab.items(.shndx)[esym_index];
17801780
1781 for (export_indices) |export_idx| {1781 for (export_indices) |export_idx| {
1782 const exp = zcu.all_exports.items[export_idx];1782 const exp = export_idx.ptr(zcu);
1783 if (exp.opts.section.unwrap()) |section_name| {1783 if (exp.opts.section.unwrap()) |section_name| {
1784 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {1784 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
1785 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1785 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
src/link/MachO/ZigObject.zig+2-2
...@@ -1259,7 +1259,7 @@ pub fn updateExports(...@@ -1259,7 +1259,7 @@ pub fn updateExports(
1259 break :blk self.navs.getPtr(nav).?;1259 break :blk self.navs.getPtr(nav).?;
1260 },1260 },
1261 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {1261 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1262 const first_exp = zcu.all_exports.items[export_indices[0]];1262 const first_exp = export_indices[0].ptr(zcu);
1263 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);1263 const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src);
1264 switch (res) {1264 switch (res) {
1265 .mcv => {},1265 .mcv => {},
...@@ -1279,7 +1279,7 @@ pub fn updateExports(...@@ -1279,7 +1279,7 @@ pub fn updateExports(
1279 const nlist = self.symtab.items(.nlist)[nlist_idx];1279 const nlist = self.symtab.items(.nlist)[nlist_idx];
12801280
1281 for (export_indices) |export_idx| {1281 for (export_indices) |export_idx| {
1282 const exp = zcu.all_exports.items[export_idx];1282 const exp = export_idx.ptr(zcu);
1283 if (exp.opts.section.unwrap()) |section_name| {1283 if (exp.opts.section.unwrap()) |section_name| {
1284 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {1284 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1285 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);1285 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
src/link/Plan9.zig+16-16
...@@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void...@@ -345,6 +345,7 @@ fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void
345 try a.writer().writeInt(u16, 1, .big);345 try a.writer().writeInt(u16, 1, .big);
346346
347 // getting the full file path347 // getting the full file path
348 // TODO don't call getcwd here, that is inappropriate
348 var buf: [std.fs.max_path_bytes]u8 = undefined;349 var buf: [std.fs.max_path_bytes]u8 = undefined;
349 const full_path = try std.fs.path.join(arena, &.{350 const full_path = try std.fs.path.join(arena, &.{
350 file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf),351 file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf),
...@@ -415,7 +416,7 @@ pub fn updateFunc(...@@ -415,7 +416,7 @@ pub fn updateFunc(
415 };416 };
416 defer dbg_info_output.dbg_line.deinit();417 defer dbg_info_output.dbg_line.deinit();
417418
418 const res = try codegen.generateFunction(419 try codegen.generateFunction(
419 &self.base,420 &self.base,
420 pt,421 pt,
421 zcu.navSrcLoc(func.owner_nav),422 zcu.navSrcLoc(func.owner_nav),
...@@ -425,10 +426,7 @@ pub fn updateFunc(...@@ -425,10 +426,7 @@ pub fn updateFunc(
425 &code_buffer,426 &code_buffer,
426 .{ .plan9 = &dbg_info_output },427 .{ .plan9 = &dbg_info_output },
427 );428 );
428 const code = switch (res) {429 const code = try code_buffer.toOwnedSlice();
429 .ok => try code_buffer.toOwnedSlice(),
430 .fail => |em| return zcu.failed_codegen.put(gpa, func.owner_nav, em),
431 };
432 self.getAtomPtr(atom_idx).code = .{430 self.getAtomPtr(atom_idx).code = .{
433 .code_ptr = null,431 .code_ptr = null,
434 .other = .{ .nav_index = func.owner_nav },432 .other = .{ .nav_index = func.owner_nav },
...@@ -439,7 +437,9 @@ pub fn updateFunc(...@@ -439,7 +437,9 @@ pub fn updateFunc(
439 .start_line = dbg_info_output.start_line.?,437 .start_line = dbg_info_output.start_line.?,
440 .end_line = dbg_info_output.end_line,438 .end_line = dbg_info_output.end_line,
441 };439 };
442 try self.putFn(func.owner_nav, out);440 // The awkward error handling here is due to putFn calling `std.posix.getcwd` which it should not do.
441 self.putFn(func.owner_nav, out) catch |err|
442 return zcu.codegenFail(func.owner_nav, "failed to put fn: {s}", .{@errorName(err)});
443 return self.updateFinish(pt, func.owner_nav);443 return self.updateFinish(pt, func.owner_nav);
444}444}
445445
...@@ -915,25 +915,25 @@ pub fn flushModule(...@@ -915,25 +915,25 @@ pub fn flushModule(
915}915}
916fn addNavExports(916fn addNavExports(
917 self: *Plan9,917 self: *Plan9,
918 mod: *Zcu,918 zcu: *Zcu,
919 nav_index: InternPool.Nav.Index,919 nav_index: InternPool.Nav.Index,
920 export_indices: []const u32,920 export_indices: []const Zcu.Export.Index,
921) !void {921) !void {
922 const gpa = self.base.comp.gpa;922 const gpa = self.base.comp.gpa;
923 const metadata = self.navs.getPtr(nav_index).?;923 const metadata = self.navs.getPtr(nav_index).?;
924 const atom = self.getAtom(metadata.index);924 const atom = self.getAtom(metadata.index);
925925
926 for (export_indices) |export_idx| {926 for (export_indices) |export_idx| {
927 const exp = mod.all_exports.items[export_idx];927 const exp = export_idx.ptr(zcu);
928 const exp_name = exp.opts.name.toSlice(&mod.intern_pool);928 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
929 // plan9 does not support custom sections929 // plan9 does not support custom sections
930 if (exp.opts.section.unwrap()) |section_name| {930 if (exp.opts.section.unwrap()) |section_name| {
931 if (!section_name.eqlSlice(".text", &mod.intern_pool) and931 if (!section_name.eqlSlice(".text", &zcu.intern_pool) and
932 !section_name.eqlSlice(".data", &mod.intern_pool))932 !section_name.eqlSlice(".data", &zcu.intern_pool))
933 {933 {
934 try mod.failed_exports.put(mod.gpa, export_idx, try Zcu.ErrorMsg.create(934 try zcu.failed_exports.put(zcu.gpa, export_idx, try Zcu.ErrorMsg.create(
935 gpa,935 gpa,
936 mod.navSrcLoc(nav_index),936 zcu.navSrcLoc(nav_index),
937 "plan9 does not support extra sections",937 "plan9 does not support extra sections",
938 .{},938 .{},
939 ));939 ));
...@@ -1252,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -1252,7 +1252,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1252 try self.writeSym(writer, sym);1252 try self.writeSym(writer, sym);
1253 if (self.nav_exports.get(nav_index)) |export_indices| {1253 if (self.nav_exports.get(nav_index)) |export_indices| {
1254 for (export_indices) |export_idx| {1254 for (export_indices) |export_idx| {
1255 const exp = zcu.all_exports.items[export_idx];1255 const exp = export_idx.ptr(zcu);
1256 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {1256 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1257 try self.writeSym(writer, self.syms.items[exp_i]);1257 try self.writeSym(writer, self.syms.items[exp_i]);
1258 }1258 }
...@@ -1291,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -1291,7 +1291,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1291 try self.writeSym(writer, sym);1291 try self.writeSym(writer, sym);
1292 if (self.nav_exports.get(nav_index)) |export_indices| {1292 if (self.nav_exports.get(nav_index)) |export_indices| {
1293 for (export_indices) |export_idx| {1293 for (export_indices) |export_idx| {
1294 const exp = zcu.all_exports.items[export_idx];1294 const exp = export_idx.ptr(zcu);
1295 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {1295 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1296 const s = self.syms.items[exp_i];1296 const s = self.syms.items[exp_i];
1297 if (mem.eql(u8, s.name, "_start"))1297 if (mem.eql(u8, s.name, "_start"))
src/link/Wasm.zig+40-54
...@@ -30,6 +30,7 @@ const log = std.log.scoped(.link);...@@ -30,6 +30,7 @@ const log = std.log.scoped(.link);
30const mem = std.mem;30const mem = std.mem;
3131
32const Air = @import("../Air.zig");32const Air = @import("../Air.zig");
33const Mir = @import("../arch/wasm/Mir.zig");
33const CodeGen = @import("../arch/wasm/CodeGen.zig");34const CodeGen = @import("../arch/wasm/CodeGen.zig");
34const Compilation = @import("../Compilation.zig");35const Compilation = @import("../Compilation.zig");
35const Dwarf = @import("Dwarf.zig");36const Dwarf = @import("Dwarf.zig");
...@@ -151,6 +152,7 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),...@@ -151,6 +152,7 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),
151preloaded_strings: PreloadedStrings,152preloaded_strings: PreloadedStrings,
152153
153navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Nav) = .empty,154navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Nav) = .empty,
155zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
154nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,156nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
155uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,157uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
156imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,158imports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
...@@ -203,6 +205,13 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, ObjectTableImportIndex) = ....@@ -203,6 +205,13 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, ObjectTableImportIndex) = .
203205
204any_exports_updated: bool = true,206any_exports_updated: bool = true,
205207
208/// All MIR instructions for all Zcu functions.
209mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
210/// Corresponds to `mir_instructions`.
211mir_extra: std.ArrayListUnmanaged(u32) = .empty,
212/// All local types for all Zcu functions.
213all_zcu_locals: std.ArrayListUnmanaged(u8) = .empty,
214
206/// Index into `objects`.215/// Index into `objects`.
207pub const ObjectIndex = enum(u32) {216pub const ObjectIndex = enum(u32) {
208 _,217 _,
...@@ -439,6 +448,24 @@ pub const Nav = extern struct {...@@ -439,6 +448,24 @@ pub const Nav = extern struct {
439 };448 };
440};449};
441450
451pub const ZcuFunc = extern struct {
452 function: CodeGen.Function,
453
454 /// Index into `zcu_funcs`.
455 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
456 pub const Index = enum(u32) {
457 _,
458
459 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
460 return &wasm.zcu_funcs.keys()[@intFromEnum(i)];
461 }
462
463 pub fn value(i: @This(), wasm: *const Wasm) *ZcuFunc {
464 return &wasm.zcu_funcs.values()[@intFromEnum(i)];
465 }
466 };
467};
468
442pub const NavExport = extern struct {469pub const NavExport = extern struct {
443 name: String,470 name: String,
444 nav_index: InternPool.Nav.Index,471 nav_index: InternPool.Nav.Index,
...@@ -932,7 +959,7 @@ pub const ValtypeList = enum(u32) {...@@ -932,7 +959,7 @@ pub const ValtypeList = enum(u32) {
932 }959 }
933960
934 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {961 pub fn slice(index: ValtypeList, wasm: *const Wasm) []const std.wasm.Valtype {
935 return @bitCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));962 return @ptrCast(String.slice(@enumFromInt(@intFromEnum(index)), wasm));
936 }963 }
937};964};
938965
...@@ -1430,12 +1457,17 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1430,12 +1457,17 @@ pub fn deinit(wasm: *Wasm) void {
1430 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();1457 if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
14311458
1432 wasm.navs.deinit(gpa);1459 wasm.navs.deinit(gpa);
1460 wasm.zcu_funcs.deinit(gpa);
1433 wasm.nav_exports.deinit(gpa);1461 wasm.nav_exports.deinit(gpa);
1434 wasm.uav_exports.deinit(gpa);1462 wasm.uav_exports.deinit(gpa);
1435 wasm.imports.deinit(gpa);1463 wasm.imports.deinit(gpa);
14361464
1437 wasm.flush_buffer.deinit(gpa);1465 wasm.flush_buffer.deinit(gpa);
14381466
1467 wasm.mir_instructions.deinit(gpa);
1468 wasm.mir_extra.deinit(gpa);
1469 wasm.all_zcu_locals.deinit(gpa);
1470
1439 if (wasm.dwarf) |*dwarf| dwarf.deinit();1471 if (wasm.dwarf) |*dwarf| dwarf.deinit();
14401472
1441 wasm.object_function_imports.deinit(gpa);1473 wasm.object_function_imports.deinit(gpa);
...@@ -1474,49 +1506,11 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,...@@ -1474,49 +1506,11 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
1474 }1506 }
1475 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);1507 if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
14761508
1477 const zcu = pt.zcu;
1478 const gpa = zcu.gpa;
1479 const func = pt.zcu.funcInfo(func_index);
1480 const nav_index = func.owner_nav;
1481
1482 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
1483 const relocs_start: u32 = @intCast(wasm.relocations.len);
1484 wasm.string_bytes_lock.lock();
1485
1486 dev.check(.wasm_backend);1509 dev.check(.wasm_backend);
1487 try CodeGen.generate(
1488 &wasm.base,
1489 pt,
1490 zcu.navSrcLoc(nav_index),
1491 func_index,
1492 air,
1493 liveness,
1494 &wasm.string_bytes,
1495 .none,
1496 );
1497
1498 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
1499 const relocs_len: u32 = @intCast(wasm.relocations.len - relocs_start);
1500 wasm.string_bytes_lock.unlock();
1501
1502 const code: Nav.Code = .{
1503 .off = code_start,
1504 .len = code_len,
1505 };
15061510
1507 const gop = try wasm.navs.getOrPut(gpa, nav_index);1511 try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{
1508 if (gop.found_existing) {1512 .function = try CodeGen.function(wasm, pt, func_index, air, liveness),
1509 @panic("TODO reuse these resources");1513 });
1510 } else {
1511 _ = wasm.imports.swapRemove(nav_index);
1512 }
1513 gop.value_ptr.* = .{
1514 .code = code,
1515 .relocs = .{
1516 .off = relocs_start,
1517 .len = relocs_len,
1518 },
1519 };
1520}1514}
15211515
1522// Generate code for the "Nav", storing it in memory to be later written to1516// Generate code for the "Nav", storing it in memory to be later written to
...@@ -1642,8 +1636,8 @@ pub fn updateExports(...@@ -1642,8 +1636,8 @@ pub fn updateExports(
1642 const exp = export_idx.ptr(zcu);1636 const exp = export_idx.ptr(zcu);
1643 const name = try wasm.internString(exp.opts.name.toSlice(ip));1637 const name = try wasm.internString(exp.opts.name.toSlice(ip));
1644 switch (exported) {1638 switch (exported) {
1645 .nav => |nav_index| wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),1639 .nav => |nav_index| try wasm.nav_exports.put(gpa, .{ .nav_index = nav_index, .name = name }, export_idx),
1646 .uav => |uav_index| wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),1640 .uav => |uav_index| try wasm.uav_exports.put(gpa, .{ .uav_index = uav_index, .name = name }, export_idx),
1647 }1641 }
1648 }1642 }
1649 wasm.any_exports_updated = true;1643 wasm.any_exports_updated = true;
...@@ -1713,9 +1707,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v...@@ -1713,9 +1707,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
1713 continue;1707 continue;
1714 }1708 }
1715 }1709 }
1716 try wasm.missing_exports.put(exp_name_interned, {});1710 try missing_exports.put(gpa, exp_name_interned, {});
1717 }1711 }
1718 wasm.missing_exports_init = try gpa.dupe(String, wasm.missing_exports.keys());1712 wasm.missing_exports_init = try gpa.dupe(String, missing_exports.keys());
1719 }1713 }
17201714
1721 if (wasm.entry_name.unwrap()) |entry_name| {1715 if (wasm.entry_name.unwrap()) |entry_name| {
...@@ -2347,14 +2341,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -2347,14 +2341,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
2347 }2341 }
2348}2342}
23492343
2350/// Returns the symbol index of the error name table.
2351///
2352/// When the symbol does not yet exist, it will create a new one instead.
2353pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 {
2354 const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt);
2355 return @intFromEnum(sym_index);
2356}
2357
2358fn defaultEntrySymbolName(2344fn defaultEntrySymbolName(
2359 preloaded_strings: *const PreloadedStrings,2345 preloaded_strings: *const PreloadedStrings,
2360 wasi_exec_model: std.builtin.WasiExecModel,2346 wasi_exec_model: std.builtin.WasiExecModel,