mirror of
https://github.com/ziglang/zig.git
synced 2026-02-01 12:13:44 +00:00
wasm: reference count locals
By reference counting the locals, we can ensure that when we free a local, no local will be reused while it still has references pointing to it. This prevents misscompilations. The compiler will also panic if we free a local more than we reference it, introducing extra safety to ensure they match up.
This commit is contained in:
parent
b9b20b14ea
commit
b17c8c5424
@ -32,8 +32,13 @@ const WValue = union(enum) {
|
||||
none: void,
|
||||
/// The value lives on top of the stack
|
||||
stack: void,
|
||||
/// Index of the local variable
|
||||
local: u32,
|
||||
/// Index of the local
|
||||
local: struct {
|
||||
/// Contains the index to the local
|
||||
value: u32,
|
||||
/// The amount of instructions referencing this `WValue`
|
||||
references: u32,
|
||||
},
|
||||
/// An immediate 32bit value
|
||||
imm32: u32,
|
||||
/// An immediate 64bit value
|
||||
@ -60,7 +65,12 @@ const WValue = union(enum) {
|
||||
function_index: u32,
|
||||
/// Offset from the bottom of the virtual stack, with the offset
|
||||
/// pointing to where the value lives.
|
||||
stack_offset: u32,
|
||||
stack_offset: struct {
|
||||
/// Contains the actual value of the offset
|
||||
value: u32,
|
||||
/// The amount of instructions referencing this `WValue`
|
||||
references: u32,
|
||||
},
|
||||
|
||||
/// Returns the offset from the bottom of the stack. This is useful when
|
||||
/// we use the load or store instruction to ensure we retrieve the value
|
||||
@ -70,7 +80,7 @@ const WValue = union(enum) {
|
||||
/// loads and stores without requiring checks everywhere.
|
||||
fn offset(self: WValue) u32 {
|
||||
switch (self) {
|
||||
.stack_offset => |stack_offset| return stack_offset,
|
||||
.stack_offset => |stack_offset| return stack_offset.value,
|
||||
else => return 0,
|
||||
}
|
||||
}
|
||||
@ -81,9 +91,9 @@ const WValue = union(enum) {
|
||||
fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue {
|
||||
switch (value) {
|
||||
.stack => {
|
||||
const local = try gen.allocLocal(ty);
|
||||
try gen.addLabel(.local_set, local.local);
|
||||
return local;
|
||||
const new_local = try gen.allocLocal(ty);
|
||||
try gen.addLabel(.local_set, new_local.local.value);
|
||||
return new_local;
|
||||
},
|
||||
.local, .stack_offset => return value,
|
||||
else => unreachable,
|
||||
@ -95,7 +105,7 @@ const WValue = union(enum) {
|
||||
/// The valtype of the local is deducted by using the index of the given `WValue`.
|
||||
fn free(value: *WValue, gen: *Self) void {
|
||||
if (value.* != .local) return;
|
||||
const local_value = value.local;
|
||||
const local_value = value.local.value;
|
||||
const reserved = gen.args.len + @boolToInt(gen.return_value != .none) + 2; // 2 for stack locals
|
||||
if (local_value < reserved) return; // reserved locals may never be re-used.
|
||||
|
||||
@ -107,7 +117,7 @@ const WValue = union(enum) {
|
||||
.f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
|
||||
.f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
|
||||
}
|
||||
value.* = WValue{ .none = {} };
|
||||
value.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@ -761,7 +771,9 @@ const BigTomb = struct {
|
||||
bt.gen.values.putAssumeCapacityNoClobber(Air.indexToRef(bt.inst), result);
|
||||
}
|
||||
|
||||
bt.gen.air_bookkeeping += 1;
|
||||
if (builtin.mode == .Debug) {
|
||||
bt.gen.air_bookkeeping += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -883,7 +895,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
|
||||
fn emitWValue(self: *Self, value: WValue) InnerError!void {
|
||||
switch (value) {
|
||||
.none, .stack => {}, // no-op
|
||||
.local => |idx| try self.addLabel(.local_get, idx),
|
||||
.local => |idx| try self.addLabel(.local_get, idx.value),
|
||||
.imm32 => |val| try self.addImm32(@bitCast(i32, val)),
|
||||
.imm64 => |val| try self.addImm64(val),
|
||||
.float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
|
||||
@ -907,16 +919,16 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
|
||||
const valtype = typeToValtype(ty, self.target);
|
||||
switch (valtype) {
|
||||
.i32 => if (self.free_locals_i32.popOrNull()) |index| {
|
||||
return WValue{ .local = index };
|
||||
return WValue{ .local = .{ .value = index, .references = 1 } };
|
||||
},
|
||||
.i64 => if (self.free_locals_i64.popOrNull()) |index| {
|
||||
return WValue{ .local = index };
|
||||
return WValue{ .local = .{ .value = index, .references = 1 } };
|
||||
},
|
||||
.f32 => if (self.free_locals_f32.popOrNull()) |index| {
|
||||
return WValue{ .local = index };
|
||||
return WValue{ .local = .{ .value = index, .references = 1 } };
|
||||
},
|
||||
.f64 => if (self.free_locals_f64.popOrNull()) |index| {
|
||||
return WValue{ .local = index };
|
||||
return WValue{ .local = .{ .value = index, .references = 1 } };
|
||||
},
|
||||
}
|
||||
// no local was free to be re-used, so allocate a new local instead
|
||||
@ -929,7 +941,7 @@ fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue {
|
||||
try self.locals.append(self.gpa, genValtype(ty, self.target));
|
||||
const initial_index = self.local_index;
|
||||
self.local_index += 1;
|
||||
return WValue{ .local = initial_index };
|
||||
return WValue{ .local = .{ .value = initial_index, .references = 1 } };
|
||||
}
|
||||
|
||||
/// Generates a `wasm.Type` from a given function type.
|
||||
@ -1059,7 +1071,7 @@ fn genFunc(self: *Self) InnerError!void {
|
||||
// load stack pointer
|
||||
try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
|
||||
// store stack pointer so we can restore it when we return from the function
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local } });
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local.value } });
|
||||
// get the total stack size
|
||||
const aligned_stack = std.mem.alignForwardGeneric(u32, self.stack_size, self.stack_alignment);
|
||||
try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } });
|
||||
@ -1070,7 +1082,7 @@ fn genFunc(self: *Self) InnerError!void {
|
||||
// Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
|
||||
try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
|
||||
// store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local } });
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local.value } });
|
||||
// Store the current stack pointer value into the global stack pointer so other function calls will
|
||||
// start from this value instead and not overwrite the current stack.
|
||||
try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
|
||||
@ -1141,7 +1153,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
|
||||
if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
|
||||
// the sret arg will be passed as first argument, therefore we
|
||||
// set the `return_value` before allocating locals for regular args.
|
||||
result.return_value = .{ .local = self.local_index };
|
||||
result.return_value = .{ .local = .{ .value = self.local_index, .references = 1 } };
|
||||
self.local_index += 1;
|
||||
}
|
||||
|
||||
@ -1152,7 +1164,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
|
||||
continue;
|
||||
}
|
||||
|
||||
try args.append(.{ .local = self.local_index });
|
||||
try args.append(.{ .local = .{ .value = self.local_index, .references = 1 } });
|
||||
self.local_index += 1;
|
||||
}
|
||||
},
|
||||
@ -1161,7 +1173,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
|
||||
const ty_classes = abi.classifyType(ty, self.target);
|
||||
for (ty_classes) |class| {
|
||||
if (class == .none) continue;
|
||||
try args.append(.{ .local = self.local_index });
|
||||
try args.append(.{ .local = .{ .value = self.local_index, .references = 1 } });
|
||||
self.local_index += 1;
|
||||
}
|
||||
}
|
||||
@ -1254,14 +1266,14 @@ fn lowerToStack(self: *Self, value: WValue) !void {
|
||||
switch (value) {
|
||||
.stack_offset => |offset| {
|
||||
try self.emitWValue(value);
|
||||
if (offset > 0) {
|
||||
if (offset.value > 0) {
|
||||
switch (self.arch()) {
|
||||
.wasm32 => {
|
||||
try self.addImm32(@bitCast(i32, offset));
|
||||
try self.addImm32(@bitCast(i32, offset.value));
|
||||
try self.addTag(.i32_add);
|
||||
},
|
||||
.wasm64 => {
|
||||
try self.addImm64(offset);
|
||||
try self.addImm64(offset.value);
|
||||
try self.addTag(.i64_add);
|
||||
},
|
||||
else => unreachable,
|
||||
@ -1323,7 +1335,7 @@ fn allocStack(self: *Self, ty: Type) !WValue {
|
||||
const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align);
|
||||
defer self.stack_size = offset + abi_size;
|
||||
|
||||
return WValue{ .stack_offset = offset };
|
||||
return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
|
||||
}
|
||||
|
||||
/// From a given AIR instruction generates a pointer to the stack where
|
||||
@ -1356,7 +1368,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment);
|
||||
defer self.stack_size = offset + abi_size;
|
||||
|
||||
return WValue{ .stack_offset = offset };
|
||||
return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
|
||||
}
|
||||
|
||||
/// From given zig bitsize, returns the wasm bitsize
|
||||
@ -1475,7 +1487,7 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
try self.addLabel(.local_set, offset.local);
|
||||
try self.addLabel(.local_set, offset.local.value);
|
||||
try self.addLabel(.br, 0); // jump to start of loop
|
||||
}
|
||||
try self.endBlock(); // close off loop block
|
||||
@ -1568,7 +1580,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
try self.addLabel(.local_set, result_ptr.local);
|
||||
try self.addLabel(.local_set, result_ptr.local.value);
|
||||
return result_ptr;
|
||||
}
|
||||
|
||||
@ -1984,14 +1996,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
|
||||
// TODO: Make this less fragile and optimize
|
||||
} else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {
|
||||
const result_local = try self.allocLocal(ret_ty);
|
||||
try self.addLabel(.local_set, result_local.local);
|
||||
try self.addLabel(.local_set, result_local.local.value);
|
||||
const scalar_type = abi.scalarType(ret_ty, self.target);
|
||||
const result = try self.allocStack(scalar_type);
|
||||
try self.store(result, result_local, scalar_type, 0);
|
||||
break :result_value result;
|
||||
} else {
|
||||
const result_local = try self.allocLocal(ret_ty);
|
||||
try self.addLabel(.local_set, result_local.local);
|
||||
try self.addLabel(.local_set, result_local.local.value);
|
||||
break :result_value result_local;
|
||||
}
|
||||
};
|
||||
@ -2175,7 +2187,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
.dwarf => |dwarf| {
|
||||
// TODO: Get the original arg index rather than wasm arg index
|
||||
const name = self.mod_fn.getParamName(self.bin_file.base.options.module.?, arg_index);
|
||||
const leb_size = link.File.Wasm.getULEB128Size(arg.local);
|
||||
const leb_size = link.File.Wasm.getULEB128Size(arg.local.value);
|
||||
const dbg_info = &dwarf.dbg_info;
|
||||
try dbg_info.ensureUnusedCapacity(3 + leb_size + 5 + name.len + 1);
|
||||
// wasm locations are encoded as follow:
|
||||
@ -2189,7 +2201,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
std.dwarf.OP.WASM_location,
|
||||
std.dwarf.OP.WASM_local,
|
||||
});
|
||||
leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable;
|
||||
leb.writeULEB128(dbg_info.writer(), arg.local.value) catch unreachable;
|
||||
try self.addDbgInfoTypeReloc(arg_ty);
|
||||
dbg_info.appendSliceAssumeCapacity(name);
|
||||
dbg_info.appendAssumeCapacity(0);
|
||||
@ -2869,7 +2881,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.lowerToStack(operand);
|
||||
|
||||
if (block.value != .none) {
|
||||
try self.addLabel(.local_set, block.value.local);
|
||||
try self.addLabel(.local_set, block.value.local.value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2893,7 +2905,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.emitWValue(operand);
|
||||
try self.addTag(.i32_eqz);
|
||||
const not_tmp = try self.allocLocal(operand_ty);
|
||||
try self.addLabel(.local_set, not_tmp.local);
|
||||
try self.addLabel(.local_set, not_tmp.local.value);
|
||||
break :result not_tmp;
|
||||
} else {
|
||||
const operand_bits = operand_ty.intInfo(self.target).bits;
|
||||
@ -2985,7 +2997,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
|
||||
fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
|
||||
switch (struct_ptr) {
|
||||
.stack_offset => |stack_offset| {
|
||||
return WValue{ .stack_offset = stack_offset + offset };
|
||||
return WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
|
||||
},
|
||||
else => return self.buildPointerOffset(struct_ptr, offset, .new),
|
||||
}
|
||||
@ -3011,7 +3023,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
if (isByRef(field_ty, self.target)) {
|
||||
switch (operand) {
|
||||
.stack_offset => |stack_offset| {
|
||||
break :result WValue{ .stack_offset = stack_offset + offset };
|
||||
break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
|
||||
},
|
||||
else => break :result try self.buildPointerOffset(operand, offset, .new),
|
||||
}
|
||||
@ -3214,7 +3226,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!v
|
||||
try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
|
||||
|
||||
const is_err_tmp = try self.allocLocal(Type.i32);
|
||||
try self.addLabel(.local_set, is_err_tmp.local);
|
||||
try self.addLabel(.local_set, is_err_tmp.local.value);
|
||||
break :result is_err_tmp;
|
||||
};
|
||||
self.finishAir(inst, result, &.{un_op});
|
||||
@ -3578,7 +3590,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.addTag(.i32_add);
|
||||
|
||||
const result_ptr = try self.allocLocal(elem_ty);
|
||||
try self.addLabel(.local_set, result_ptr.local);
|
||||
try self.addLabel(.local_set, result_ptr.local.value);
|
||||
|
||||
const result = if (!isByRef(elem_ty, self.target)) result: {
|
||||
const elem_val = try self.load(result_ptr, elem_ty, 0);
|
||||
@ -3608,7 +3620,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.addTag(.i32_add);
|
||||
|
||||
const result = try self.allocLocal(Type.i32);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -3715,7 +3727,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
|
||||
const elem_result = val: {
|
||||
var result = try self.allocLocal(elem_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
if (isByRef(elem_ty, self.target)) {
|
||||
break :val result;
|
||||
}
|
||||
@ -3753,7 +3765,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.addTag(.i32_add);
|
||||
|
||||
const result = try self.allocLocal(Type.i32);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -3781,7 +3793,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
|
||||
try self.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
|
||||
|
||||
const result = try self.allocLocal(Type.usize);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -3874,7 +3886,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
|
||||
.wasm64 => try self.addTag(.i64_add),
|
||||
else => unreachable,
|
||||
}
|
||||
try self.addLabel(.local_set, offset.local);
|
||||
try self.addLabel(.local_set, offset.local.value);
|
||||
try self.addLabel(.br, 0); // jump to start of loop
|
||||
try self.endBlock();
|
||||
try self.endBlock();
|
||||
@ -3900,7 +3912,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
|
||||
const elem_result = val: {
|
||||
var result = try self.allocLocal(Type.usize);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
|
||||
if (isByRef(elem_ty, self.target)) {
|
||||
break :val result;
|
||||
@ -3961,7 +3973,7 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.addTag(Mir.Inst.Tag.fromOpcode(op));
|
||||
|
||||
const result = try self.allocLocal(dest_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ty_op.operand});
|
||||
}
|
||||
|
||||
@ -4107,7 +4119,7 @@ fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
|
||||
const result = try self.allocLocal(self.air.typeOfIndex(inst));
|
||||
try self.addLabel(.memory_size, pl_op.payload);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{pl_op.operand});
|
||||
}
|
||||
|
||||
@ -4119,7 +4131,7 @@ fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const result = try self.allocLocal(self.air.typeOfIndex(inst));
|
||||
try self.emitWValue(operand);
|
||||
try self.addLabel(.memory_grow, pl_op.payload);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{pl_op.operand});
|
||||
}
|
||||
|
||||
@ -4148,7 +4160,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
|
||||
try self.addLabel(.br_if, 0);
|
||||
|
||||
try self.addImm32(1);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
try self.endBlock();
|
||||
|
||||
try self.emitWValue(result);
|
||||
@ -4363,10 +4375,10 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
|
||||
const result = if (field_offset != 0) result: {
|
||||
const base = try self.buildPointerOffset(field_ptr, 0, .new);
|
||||
try self.addLabel(.local_get, base.local);
|
||||
try self.addLabel(.local_get, base.local.value);
|
||||
try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
|
||||
try self.addTag(.i32_sub);
|
||||
try self.addLabel(.local_set, base.local);
|
||||
try self.addLabel(.local_set, base.local.value);
|
||||
break :result base;
|
||||
} else field_ptr;
|
||||
|
||||
@ -4425,7 +4437,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
|
||||
const result = try self.allocLocal(result_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ty_op.operand});
|
||||
}
|
||||
|
||||
@ -4467,7 +4479,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
|
||||
const result_ptr = try self.allocLocal(Type.usize);
|
||||
try self.addLabel(.local_set, result_ptr.local);
|
||||
try self.addLabel(.local_set, result_ptr.local.value);
|
||||
self.finishAir(inst, result_ptr, &.{un_op});
|
||||
}
|
||||
|
||||
@ -4714,7 +4726,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
|
||||
const wrap = try self.intcast(shr, new_ty, lhs_ty);
|
||||
_ = try self.cmp(wrap, zero, lhs_ty, .neq);
|
||||
try self.addLabel(.local_set, overflow_bit.local);
|
||||
try self.addLabel(.local_set, overflow_bit.local.value);
|
||||
break :blk try self.intcast(bin_op, new_ty, lhs_ty);
|
||||
} else {
|
||||
const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty);
|
||||
@ -4724,7 +4736,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
|
||||
const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);
|
||||
_ = try self.cmp(down_shr_res, shr, lhs_ty, .neq);
|
||||
try self.addLabel(.local_set, overflow_bit.local);
|
||||
try self.addLabel(.local_set, overflow_bit.local.value);
|
||||
break :blk down_cast;
|
||||
}
|
||||
} else if (int_info.signedness == .signed) blk: {
|
||||
@ -4733,7 +4745,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty);
|
||||
const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
|
||||
_ = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
|
||||
try self.addLabel(.local_set, overflow_bit.local);
|
||||
try self.addLabel(.local_set, overflow_bit.local.value);
|
||||
break :blk try self.wrapOperand(bin_op, lhs_ty);
|
||||
} else blk: {
|
||||
var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);
|
||||
@ -4744,7 +4756,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
WValue{ .imm64 = int_info.bits };
|
||||
const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr);
|
||||
_ = try self.cmp(shr, zero, lhs_ty, .neq);
|
||||
try self.addLabel(.local_set, overflow_bit.local);
|
||||
try self.addLabel(.local_set, overflow_bit.local.value);
|
||||
break :blk try self.wrapOperand(bin_op, lhs_ty);
|
||||
};
|
||||
var bin_op_local = try bin_op.toLocal(self, lhs_ty);
|
||||
@ -4785,7 +4797,7 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro
|
||||
// store result in local
|
||||
const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty;
|
||||
const result = try self.allocLocal(result_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -4872,7 +4884,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
|
||||
const result = try self.allocLocal(result_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ty_op.operand});
|
||||
}
|
||||
|
||||
@ -4937,7 +4949,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
|
||||
const result = try self.allocLocal(result_ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ty_op.operand});
|
||||
}
|
||||
|
||||
@ -4958,7 +4970,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !void {
|
||||
try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
|
||||
switch (operand) {
|
||||
.local => |local| {
|
||||
const leb_size = link.File.Wasm.getULEB128Size(local);
|
||||
const leb_size = link.File.Wasm.getULEB128Size(local.value);
|
||||
try dbg_info.ensureUnusedCapacity(2 + leb_size);
|
||||
// wasm locals are encoded as follow:
|
||||
// DW_OP_WASM_location wasm-op
|
||||
@ -4970,7 +4982,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !void {
|
||||
std.dwarf.OP.WASM_location,
|
||||
std.dwarf.OP.WASM_local,
|
||||
});
|
||||
leb.writeULEB128(dbg_info.writer(), local) catch unreachable;
|
||||
leb.writeULEB128(dbg_info.writer(), local.value) catch unreachable;
|
||||
},
|
||||
else => {}, // TODO
|
||||
}
|
||||
@ -5179,7 +5191,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
const div_result = try self.allocLocal(ty);
|
||||
// leave on stack
|
||||
_ = try self.binOp(lhs_res, rhs_res, ty, .div);
|
||||
try self.addLabel(.local_tee, div_result.local);
|
||||
try self.addLabel(.local_tee, div_result.local.value);
|
||||
_ = try self.cmp(lhs_res, zero, ty, .lt);
|
||||
_ = try self.cmp(rhs_res, zero, ty, .lt);
|
||||
switch (wasm_bits) {
|
||||
@ -5232,7 +5244,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
|
||||
const result = try self.allocLocal(ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -5257,7 +5269,7 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue
|
||||
try self.addTag(.i32_div_s);
|
||||
|
||||
const result = try self.allocLocal(ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -5323,7 +5335,7 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void
|
||||
}
|
||||
|
||||
const result = try self.allocLocal(ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
self.finishAir(inst, result, &.{un_op});
|
||||
}
|
||||
|
||||
@ -5374,7 +5386,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
|
||||
|
||||
try self.addTag(.select);
|
||||
const result = try self.allocLocal(ty);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
}
|
||||
|
||||
@ -5412,13 +5424,13 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
|
||||
try self.emitWValue(max_wvalue);
|
||||
_ = try self.cmp(bin_result, max_wvalue, ty, .lt);
|
||||
try self.addTag(.select);
|
||||
try self.addLabel(.local_set, bin_result.local); // re-use local
|
||||
try self.addLabel(.local_set, bin_result.local.value); // re-use local
|
||||
|
||||
try self.emitWValue(bin_result);
|
||||
try self.emitWValue(min_wvalue);
|
||||
_ = try self.cmp(bin_result, min_wvalue, ty, .gt);
|
||||
try self.addTag(.select);
|
||||
try self.addLabel(.local_set, bin_result.local); // re-use local
|
||||
try self.addLabel(.local_set, bin_result.local.value); // re-use local
|
||||
return (try self.wrapOperand(bin_result, ty)).toLocal(self, ty);
|
||||
} else {
|
||||
const zero = switch (wasm_bits) {
|
||||
@ -5436,7 +5448,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
|
||||
const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
|
||||
_ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
|
||||
try self.addTag(.select);
|
||||
try self.addLabel(.local_set, bin_result.local); // re-use local
|
||||
try self.addLabel(.local_set, bin_result.local.value); // re-use local
|
||||
return bin_result;
|
||||
}
|
||||
}
|
||||
@ -5489,7 +5501,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.emitWValue(shl);
|
||||
_ = try self.cmp(lhs, shr, ty, .neq);
|
||||
try self.addTag(.select);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
break :outer_blk;
|
||||
} else {
|
||||
const shift_size = wasm_bits - int_info.bits;
|
||||
@ -5534,12 +5546,12 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
|
||||
try self.emitWValue(shl);
|
||||
_ = try self.cmp(shl_res, shr, ty, .neq);
|
||||
try self.addTag(.select);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
var shift_result = try self.binOp(result, shift_value, ty, .shr);
|
||||
if (is_signed) {
|
||||
shift_result = try self.wrapOperand(shift_result, ty);
|
||||
}
|
||||
try self.addLabel(.local_set, result.local);
|
||||
try self.addLabel(.local_set, result.local.value);
|
||||
}
|
||||
|
||||
return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user