wasm codegen: fix some compilation errors

This commit is contained in:
Andrew Kelley 2024-12-06 18:27:00 -08:00
parent e521879e47
commit bffa148600
5 changed files with 161 additions and 106 deletions

View File

@ -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,
};
}

View File

@ -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);

View File

@ -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,16 +45,23 @@ 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];
},
.nav_ref => {
try navRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });
inst += 1;
continue :loop tags[inst];
},
.nav_ref_off => {
try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data);
inst += 1;
continue :loop tags[inst];
},
@ -60,10 +70,6 @@ pub fn lowerToCode(emit: *Emit) Error!void {
inst += 1;
continue :loop tags[inst];
},
.dbg_epilogue_begin => {
return;
},
.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;
}
}
}

View File

@ -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,

View File

@ -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);