Commit bffa148600
Changed files (5)
src
src/arch/wasm/CodeGen.zig
@@ -146,19 +146,14 @@ const WValue = union(enum) {
float32: f32,
/// A constant 64bit float value
float64: f64,
- /// A value that represents a pointer to the data section.
- memory: InternPool.Index,
- /// A value that represents a parent pointer and an offset
- /// from that pointer. i.e. when slicing with constant values.
- memory_offset: struct {
- pointer: InternPool.Index,
- /// Offset will be set as addend when relocating
- offset: u32,
+ nav_ref: struct {
+ nav_index: InternPool.Nav.Index,
+ offset: i32 = 0,
+ },
+ uav_ref: struct {
+ ip_index: InternPool.Index,
+ offset: i32 = 0,
},
- /// Represents a function pointer
- /// In wasm function pointers are indexes into a function table,
- /// rather than an address in the data section.
- function_index: InternPool.Index,
/// Offset from the bottom of the virtual stack, with the offset
/// pointing to where the value lives.
stack_offset: struct {
@@ -752,7 +747,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
const ty = func.typeOf(ref);
if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) {
gop.value_ptr.* = .none;
- return gop.value_ptr.*;
+ return .none;
}
// When we need to pass the value by reference (such as a struct), we will
@@ -762,7 +757,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
// In the other cases, we will simply lower the constant to a value that fits
// into a single local (such as a pointer, integer, bool, etc).
const result: WValue = if (isByRef(ty, pt, func.target))
- .{ .memory = val.toIntern() }
+ .{ .uav_ref = .{ .ip_index = val.toIntern() } }
else
try func.lowerConstant(val, ty);
@@ -956,6 +951,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
u32 => @field(extra, field.name),
i32 => @bitCast(@field(extra, field.name)),
InternPool.Index => @intFromEnum(@field(extra, field.name)),
+ InternPool.Nav.Index => @intFromEnum(@field(extra, field.name)),
else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
});
}
@@ -1028,17 +1024,36 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
.imm128 => |val| try func.addImm128(val),
.float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
.float64 => |val| try func.addFloat64(val),
- .memory => |ptr| try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = ptr } }),
- .memory_offset => |mo| try func.addInst(.{
- .tag = .uav_ref_off,
- .data = .{
- .payload = try func.addExtra(Mir.UavRefOff{
- .ip_index = mo.pointer,
- .offset = @intCast(mo.offset), // TODO should not be an assert
- }),
- },
- }),
- .function_index => |index| try func.addIpIndex(.function_index, index),
+ .nav_ref => |nav_ref| {
+ if (nav_ref.offset == 0) {
+ try func.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } });
+ } else {
+ try func.addInst(.{
+ .tag = .nav_ref_off,
+ .data = .{
+ .payload = try func.addExtra(Mir.NavRefOff{
+ .nav_index = nav_ref.nav_index,
+ .offset = nav_ref.offset,
+ }),
+ },
+ });
+ }
+ },
+ .uav_ref => |uav| {
+ if (uav.offset == 0) {
+ try func.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = uav.ip_index } });
+ } else {
+ try func.addInst(.{
+ .tag = .uav_ref_off,
+ .data = .{
+ .payload = try func.addExtra(Mir.UavRefOff{
+ .ip_index = uav.ip_index,
+ .offset = uav.offset,
+ }),
+ },
+ });
+ }
+ },
.stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
}
}
@@ -1466,10 +1481,7 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value:
assert(ty_classes[0] == .direct);
const scalar_type = abi.scalarType(ty, zcu);
switch (value) {
- .memory,
- .memory_offset,
- .stack_offset,
- => _ = try func.load(value, scalar_type, 0),
+ .nav_ref, .stack_offset => _ = try func.load(value, scalar_type, 0),
.dead => unreachable,
else => try func.emitWValue(value),
}
@@ -3117,8 +3129,8 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
const offset: u64 = prev_offset + ptr.byte_offset;
return switch (ptr.base_addr) {
- .nav => |nav| return func.lowerNavRef(nav, @intCast(offset)),
- .uav => |uav| return func.lowerUavRef(uav, @intCast(offset)),
+ .nav => |nav| return .{ .nav_ref = .{ .nav_index = zcu.chaseNav(nav), .offset = @intCast(offset) } },
+ .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset) } },
.int => return func.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize),
.eu_payload => return func.fail("Wasm TODO: lower error union payload pointer", .{}),
.opt_payload => |opt_ptr| return func.lowerPtr(opt_ptr, offset),
@@ -3162,51 +3174,6 @@ fn lowerPtr(func: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerEr
};
}
-fn lowerUavRef(
- func: *CodeGen,
- uav: InternPool.Key.Ptr.BaseAddr.Uav,
- offset: u32,
-) InnerError!WValue {
- const pt = func.pt;
- const zcu = pt.zcu;
- const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav.val));
-
- const is_fn_body = ty.zigTypeTag(zcu) == .@"fn";
- if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(zcu)) {
- return .{ .imm32 = 0xaaaaaaaa };
- }
-
- return if (is_fn_body) .{
- .function_index = uav.val,
- } else if (offset == 0) .{
- .memory = uav.val,
- } else .{ .memory_offset = .{
- .pointer = uav.val,
- .offset = offset,
- } };
-}
-
-fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) InnerError!WValue {
- const pt = func.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
-
- const nav_ty = ip.getNav(nav_index).typeOf(ip);
- if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
- return .{ .imm32 = 0xaaaaaaaa };
- }
-
- const atom_index = try func.wasm.getOrCreateAtomForNav(pt, nav_index);
- const atom = func.wasm.getAtom(atom_index);
-
- const target_sym_index = @intFromEnum(atom.sym_index);
- if (ip.isFunctionType(nav_ty)) {
- return .{ .function_index = target_sym_index };
- } else if (offset == 0) {
- return .{ .memory = target_sym_index };
- } else return .{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
-}
-
/// Asserts that `isByRef` returns `false` for `ty`.
fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
const pt = func.pt;
@@ -3307,7 +3274,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
.f64 => |f64_val| return .{ .float64 = f64_val },
else => unreachable,
},
- .slice => return .{ .memory = val.toIntern() },
+ .slice => unreachable, // isByRef == true
.ptr => return func.lowerPtr(val.toIntern(), 0),
.opt => if (ty.optionalReprIsPayload(zcu)) {
const pl_ty = ty.optionalChild(zcu);
src/arch/wasm/Emit.zig
@@ -33,6 +33,9 @@ pub fn lowerToCode(emit: *Emit) Error!void {
var inst: u32 = 0;
loop: switch (tags[inst]) {
+ .dbg_epilogue_begin => {
+ return;
+ },
.block, .loop => {
const block_type = datas[inst].block_type;
try code.ensureUnusedCapacity(gpa, 2);
@@ -42,28 +45,31 @@ pub fn lowerToCode(emit: *Emit) Error!void {
inst += 1;
continue :loop tags[inst];
},
-
.uav_ref => {
try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
-
inst += 1;
continue :loop tags[inst];
},
.uav_ref_off => {
try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);
-
inst += 1;
continue :loop tags[inst];
},
-
- .dbg_line => {
+ .nav_ref => {
+ try navRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
inst += 1;
continue :loop tags[inst];
},
- .dbg_epilogue_begin => {
- return;
+ .nav_ref_off => {
+ try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data);
+ inst += 1;
+ continue :loop tags[inst];
},
+ .dbg_line => {
+ inst += 1;
+ continue :loop tags[inst];
+ },
.br_if, .br, .memory_grow, .memory_size => {
try code.ensureUnusedCapacity(gpa, 11);
code.appendAssumeCapacity(@intFromEnum(tags[inst]));
@@ -431,7 +437,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
_ => unreachable,
}
- unreachable;
+ comptime unreachable;
},
.simd_prefix => {
try code.ensureUnusedCapacity(gpa, 6 + 20);
@@ -487,7 +493,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
},
_ => unreachable,
}
- unreachable;
+ comptime unreachable;
},
.atomics_prefix => {
try code.ensureUnusedCapacity(gpa, 6 + 20);
@@ -576,13 +582,13 @@ pub fn lowerToCode(emit: *Emit) Error!void {
continue :loop tags[inst];
},
}
- unreachable;
+ comptime unreachable;
},
}
- unreachable;
+ comptime unreachable;
}
-/// Assert 20 unused capacity.
+/// Asserts 20 unused capacity.
fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
assert(code.unusedCapacitySlice().len >= 20);
// Wasm encodes alignment as power of 2, rather than natural alignment.
@@ -619,3 +625,48 @@ fn uavRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir
const addr: i64 = try wasm.uavAddr(data.ip_index);
leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
}
+
+fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff) !void {
+ const comp = wasm.base.comp;
+ const zcu = comp.zcu.?;
+ const ip = &zcu.intern_pool;
+ const gpa = comp.gpa;
+ const is_obj = comp.config.output_mode == .Obj;
+ const target = &comp.root_mod.resolved_target.result;
+ const nav_ty = ip.getNav(data.nav_index).typeOf(ip);
+
+ try code.ensureUnusedCapacity(gpa, 11);
+
+ if (ip.isFunctionType(nav_ty)) {
+ code.appendAssumeCapacity(std.wasm.Opcode.i32_const);
+ assert(data.offset == 0);
+ if (is_obj) {
+ try wasm.out_relocs.append(gpa, .{
+ .offset = @intCast(code.items.len),
+ .index = try wasm.navSymbolIndex(data.nav_index),
+ .tag = .TABLE_INDEX_SLEB,
+ .addend = data.offset,
+ });
+ code.appendNTimesAssumeCapacity(0, 5);
+ } else {
+ const addr: i64 = try wasm.navAddr(data.nav_index);
+ leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
+ }
+ } else {
+ const is_wasm32 = target.cpu.arch == .wasm32;
+ const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
+ code.appendAssumeCapacity(@intFromEnum(opcode));
+ if (is_obj) {
+ try wasm.out_relocs.append(gpa, .{
+ .offset = @intCast(code.items.len),
+ .index = try wasm.navSymbolIndex(data.nav_index),
+ .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
+ .addend = data.offset,
+ });
+ code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
+ } else {
+ const addr: i64 = try wasm.navAddr(data.nav_index);
+ leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
+ }
+ }
+}
src/arch/wasm/Mir.zig
@@ -32,8 +32,12 @@ pub const Inst = struct {
/// Some tags match wasm opcode values to facilitate trivial lowering.
pub const Tag = enum(u8) {
- /// Uses `nop`
+ /// Uses `tag`.
@"unreachable" = 0x00,
+ /// Emits epilogue begin debug information. Marks the end of the function.
+ ///
+ /// Uses `tag` (no additional data).
+ dbg_epilogue_begin,
/// Creates a new block that can be jump from.
///
/// Type of the block is given in data `block_type`
@@ -46,34 +50,51 @@ pub const Inst = struct {
/// memory address of an unnamed constant. When emitting an object
/// file, this adds a relocation.
///
- /// Data is `ip_index`.
+ /// This may not refer to a function.
+ ///
+ /// Uses `ip_index`.
uav_ref,
/// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
/// memory address of an unnamed constant, offset by an integer value.
/// When emitting an object file, this adds a relocation.
///
- /// Data is `payload` pointing to a `UavRefOff`.
+ /// This may not refer to a function.
+ ///
+ /// Uses `payload` pointing to a `UavRefOff`.
uav_ref_off,
+ /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
+ /// memory address of a named constant.
+ ///
+ /// When this refers to a function, this always lowers to an i32_const
+ /// which is the function index. When emitting an object file, this
+ /// adds a `Wasm.Relocation.Tag.TABLE_INDEX_SLEB` relocation.
+ ///
+ /// Uses `nav_index`.
+ nav_ref,
+ /// Lowers to an i32_const (wasm32) or i64_const (wasm64) which is the
+ /// memory address of named constant, offset by an integer value.
+ /// When emitting an object file, this adds a relocation.
+ ///
+ /// This may not refer to a function.
+ ///
+ /// Uses `payload` pointing to a `NavRefOff`.
+ nav_ref_off,
/// Inserts debug information about the current line and column
/// of the source code
///
/// Uses `payload` of which the payload type is `DbgLineColumn`
- dbg_line = 0x06,
- /// Emits epilogue begin debug information. Marks the end of the function.
- ///
- /// Uses `nop`
- dbg_epilogue_begin = 0x07,
+ dbg_line,
/// Represents the end of a function body or an initialization expression
///
- /// Payload is `nop`
+ /// Uses `tag` (no additional data).
end = 0x0B,
/// Breaks from the current block to a label
///
- /// Data is `label` where index represents the label to jump to
+ /// Uses `label` where index represents the label to jump to
br = 0x0C,
/// Breaks from the current block if the stack value is non-zero
///
- /// Data is `label` where index represents the label to jump to
+ /// Uses `label` where index represents the label to jump to
br_if = 0x0D,
/// Jump table that takes the stack value as an index where each value
/// represents the label to jump to.
@@ -82,7 +103,7 @@ pub const Inst = struct {
br_table = 0x0E,
/// Returns from the function
///
- /// Uses `nop`
+ /// Uses `tag`.
@"return" = 0x0F,
/// Calls a function using `nav_index`.
call_nav,
@@ -98,10 +119,6 @@ pub const Inst = struct {
/// The function is the auto-generated tag name function for the type
/// provided in `ip_index`.
call_tag_name,
- /// Lowers to an i32_const containing the index of a function.
- /// When emitting an object file, this adds a relocation.
- /// Uses `ip_index`.
- function_index,
/// Pops three values from the stack and pushes
/// the first or second value dependent on the third value.
@@ -663,6 +680,11 @@ pub const UavRefOff = struct {
offset: i32,
};
+pub const NavRefOff = struct {
+ nav_index: InternPool.Nav.Index,
+ offset: i32,
+};
+
/// Maps a source line with wasm bytecode
pub const DbgLineColumn = struct {
line: u32,
src/link/Wasm.zig
@@ -1508,6 +1508,10 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index,
dev.check(.wasm_backend);
+ // This converts AIR to MIR but does not yet lower to wasm code.
+ // That lowering happens during `flush`, after garbage collection, which
+ // can affect function and global indexes, which affects the LEB integer
+ // encoding, which affects the output binary size.
try wasm.zcu_funcs.put(pt.zcu.gpa, func_index, .{
.function = try CodeGen.function(wasm, pt, func_index, air, liveness),
});
@@ -1729,7 +1733,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
continue;
}
}
- wasm.functions_len = @intCast(wasm.functions.items.len);
+ wasm.functions_len = @intCast(wasm.functions.entries.len);
wasm.function_imports_init_keys = try gpa.dupe(String, wasm.function_imports.keys());
wasm.function_imports_init_vals = try gpa.dupe(FunctionImportId, wasm.function_imports.vals());
wasm.function_exports_len = @intCast(wasm.function_exports.items.len);
src/Zcu.zig
@@ -4120,3 +4120,14 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg)
zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
return error.CodegenFail;
}
+
+/// Check if nav is an alias to a function, in which case we want to lower the
+/// actual nav, rather than the alias itself.
+pub fn chaseNav(zcu: *const Zcu, nav: InternPool.Nav.Index) InternPool.Nav.Index {
+ return switch (zcu.intern_pool.indexToKey(zcu.navValue(nav).toIntern())) {
+ .func => |f| f.owner_nav,
+ .variable => |variable| variable.owner_nav,
+ .@"extern" => |@"extern"| @"extern".owner_nav,
+ else => nav,
+ };
+}