Merge remote-tracking branch 'origin/master' into ast-memory-layout
This commit is contained in:
@@ -317,7 +317,7 @@ pub const Manifest = struct {
|
||||
cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
|
||||
cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
|
||||
cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
|
||||
std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
|
||||
_ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
|
||||
|
||||
if (file_path.len == 0) {
|
||||
return error.InvalidFormat;
|
||||
|
||||
@@ -645,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
};
|
||||
|
||||
const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: {
|
||||
const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: {
|
||||
const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: {
|
||||
// TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out.
|
||||
// See https://github.com/ziglang/zig/issues/6996
|
||||
const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11;
|
||||
@@ -1538,7 +1538,9 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
|
||||
}
|
||||
|
||||
pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
|
||||
var progress: std.Progress = .{};
|
||||
// If the terminal is dumb, we dont want to show the user all the
|
||||
// output.
|
||||
var progress: std.Progress = .{ .dont_print_on_dumb = true };
|
||||
var main_progress_node = try progress.start("", 0);
|
||||
defer main_progress_node.end();
|
||||
if (self.color == .off) progress.terminal = null;
|
||||
|
||||
119
src/codegen.zig
119
src/codegen.zig
@@ -944,7 +944,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
/// Copies a value to a register without tracking the register. The register is not considered
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
|
||||
fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register {
|
||||
const reg = self.findUnusedReg() orelse b: {
|
||||
// We'll take over the first register. Move the instruction that was previously
|
||||
// there to a stack allocation.
|
||||
@@ -961,7 +961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
break :b reg;
|
||||
};
|
||||
try self.genSetReg(src, reg, mcv);
|
||||
try self.genSetReg(src, ty, reg, mcv);
|
||||
return reg;
|
||||
}
|
||||
|
||||
@@ -988,7 +988,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
break :b reg;
|
||||
};
|
||||
try self.genSetReg(reg_owner.src, reg, mcv);
|
||||
try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
|
||||
@@ -1356,13 +1356,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// Load immediate into register if it doesn't fit
|
||||
// as an operand
|
||||
break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse
|
||||
Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none);
|
||||
Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none);
|
||||
},
|
||||
.register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
|
||||
.stack_offset,
|
||||
.embedded_in_code,
|
||||
.memory,
|
||||
=> Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none),
|
||||
=> Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none),
|
||||
};
|
||||
|
||||
switch (op) {
|
||||
@@ -1448,7 +1448,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
switch (src_mcv) {
|
||||
.immediate => |imm| {
|
||||
if (imm > math.maxInt(u31)) {
|
||||
src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
|
||||
src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) };
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
@@ -1479,7 +1479,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.register => |dst_reg| {
|
||||
switch (src_mcv) {
|
||||
.none => unreachable,
|
||||
.undef => try self.genSetReg(src, dst_reg, .undef),
|
||||
.undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),
|
||||
.dead, .unreach => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
@@ -1689,7 +1689,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
switch (mc_arg) {
|
||||
.none => continue,
|
||||
.register => |reg| {
|
||||
try self.genSetReg(arg.src, reg, arg_mcv);
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
// TODO interact with the register allocator to mark the instruction as moved.
|
||||
},
|
||||
.stack_offset => {
|
||||
@@ -1758,7 +1758,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
else
|
||||
unreachable;
|
||||
|
||||
try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
|
||||
try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr });
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
|
||||
} else if (func_value.castTag(.extern_fn)) |_| {
|
||||
return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
|
||||
@@ -1831,7 +1831,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.compare_flags_signed => unreachable,
|
||||
.compare_flags_unsigned => unreachable,
|
||||
.register => |reg| {
|
||||
try self.genSetReg(arg.src, reg, arg_mcv);
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
// TODO interact with the register allocator to mark the instruction as moved.
|
||||
},
|
||||
.stack_offset => {
|
||||
@@ -1859,7 +1859,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
else
|
||||
unreachable;
|
||||
|
||||
try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
|
||||
try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr });
|
||||
|
||||
// TODO: add Instruction.supportedOn
|
||||
// function for ARM
|
||||
@@ -1894,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.compare_flags_signed => unreachable,
|
||||
.compare_flags_unsigned => unreachable,
|
||||
.register => |reg| {
|
||||
try self.genSetReg(arg.src, reg, arg_mcv);
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
// TODO interact with the register allocator to mark the instruction as moved.
|
||||
},
|
||||
.stack_offset => {
|
||||
@@ -1922,7 +1922,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
else
|
||||
unreachable;
|
||||
|
||||
try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr });
|
||||
try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr });
|
||||
|
||||
writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
|
||||
} else if (func_value.castTag(.extern_fn)) |_| {
|
||||
@@ -1945,7 +1945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
switch (mc_arg) {
|
||||
.none => continue,
|
||||
.register => |reg| {
|
||||
try self.genSetReg(arg.src, reg, arg_mcv);
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
// TODO interact with the register allocator to mark the instruction as moved.
|
||||
},
|
||||
.stack_offset => {
|
||||
@@ -1978,12 +1978,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
|
||||
switch (arch) {
|
||||
.x86_64 => {
|
||||
try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
|
||||
try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
|
||||
// callq *%rax
|
||||
self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
|
||||
},
|
||||
.aarch64 => {
|
||||
try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr });
|
||||
try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr });
|
||||
// blr x30
|
||||
writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
|
||||
},
|
||||
@@ -2584,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "svc #0")) {
|
||||
@@ -2614,7 +2614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "svc #0")) {
|
||||
@@ -2646,7 +2646,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "ecall")) {
|
||||
@@ -2676,7 +2676,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
|
||||
const arg = try self.resolveInst(inst.args[i]);
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
|
||||
}
|
||||
|
||||
if (mem.eql(u8, inst.asm_source, "syscall")) {
|
||||
@@ -2738,7 +2738,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
|
||||
switch (loc) {
|
||||
.none => return,
|
||||
.register => |reg| return self.genSetReg(src, reg, val),
|
||||
.register => |reg| return self.genSetReg(src, ty, reg, val),
|
||||
.stack_offset => |off| return self.genSetStack(src, ty, off, val),
|
||||
.memory => {
|
||||
return self.fail(src, "TODO implement setRegOrMem for memory", .{});
|
||||
@@ -2773,7 +2773,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
|
||||
},
|
||||
.immediate => {
|
||||
const reg = try self.copyToTmpRegister(src, mcv);
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
.embedded_in_code => |code_offset| {
|
||||
@@ -2787,7 +2787,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
1, 4 => {
|
||||
const offset = if (math.cast(u12, adj_off)) |imm| blk: {
|
||||
break :blk Instruction.Offset.imm(imm);
|
||||
} else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
|
||||
} else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
|
||||
const str = switch (abi_size) {
|
||||
1 => Instruction.strb,
|
||||
4 => Instruction.str,
|
||||
@@ -2802,7 +2802,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
2 => {
|
||||
const offset = if (adj_off <= math.maxInt(u8)) blk: {
|
||||
break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
|
||||
} else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
|
||||
} else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
|
||||
|
||||
writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
|
||||
.offset = offset,
|
||||
@@ -2819,7 +2819,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
|
||||
const reg = try self.copyToTmpRegister(src, mcv);
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
},
|
||||
@@ -2908,7 +2908,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
|
||||
const reg = try self.copyToTmpRegister(src, mcv);
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
},
|
||||
@@ -2936,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
|
||||
},
|
||||
.immediate => {
|
||||
const reg = try self.copyToTmpRegister(src, mcv);
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
.embedded_in_code => |code_offset| {
|
||||
@@ -2951,7 +2951,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const offset = if (math.cast(i9, adj_off)) |imm|
|
||||
Instruction.LoadStoreOffset.imm_post_index(-imm)
|
||||
else |_|
|
||||
Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
|
||||
Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
|
||||
const rn: Register = switch (arch) {
|
||||
.aarch64, .aarch64_be => .x29,
|
||||
.aarch64_32 => .w29,
|
||||
@@ -2972,7 +2972,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
|
||||
const reg = try self.copyToTmpRegister(src, mcv);
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
},
|
||||
@@ -2980,7 +2980,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
|
||||
fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
|
||||
switch (arch) {
|
||||
.arm, .armeb => switch (mcv) {
|
||||
.dead => unreachable,
|
||||
@@ -2991,7 +2991,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (!self.wantSafety())
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
|
||||
return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa });
|
||||
},
|
||||
.compare_flags_unsigned,
|
||||
.compare_flags_signed,
|
||||
@@ -3056,21 +3056,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.memory => |addr| {
|
||||
// The value is in memory at a hard-coded address.
|
||||
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||
try self.genSetReg(src, reg, .{ .immediate = addr });
|
||||
try self.genSetReg(src, ty, reg, .{ .immediate = addr });
|
||||
writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
|
||||
},
|
||||
.stack_offset => |unadjusted_off| {
|
||||
// TODO: maybe addressing from sp instead of fp
|
||||
// TODO: supply type information to genSetReg as we do to genSetStack
|
||||
// const abi_size = ty.abiSize(self.target.*);
|
||||
const abi_size = 4;
|
||||
const abi_size = ty.abiSize(self.target.*);
|
||||
const adj_off = unadjusted_off + abi_size;
|
||||
|
||||
switch (abi_size) {
|
||||
1, 4 => {
|
||||
const offset = if (adj_off <= math.maxInt(u12)) blk: {
|
||||
break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
|
||||
} else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
|
||||
} else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
|
||||
const ldr = switch (abi_size) {
|
||||
1 => Instruction.ldrb,
|
||||
4 => Instruction.ldr,
|
||||
@@ -3085,7 +3083,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
2 => {
|
||||
const offset = if (adj_off <= math.maxInt(u8)) blk: {
|
||||
break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
|
||||
} else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
|
||||
} else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
|
||||
|
||||
writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
|
||||
.offset = offset,
|
||||
@@ -3107,8 +3105,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
switch (reg.size()) {
|
||||
32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
|
||||
64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
|
||||
32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
|
||||
64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
|
||||
else => unreachable, // unexpected register size
|
||||
}
|
||||
},
|
||||
@@ -3221,7 +3219,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
} else {
|
||||
// The value is in memory at a hard-coded address.
|
||||
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||
try self.genSetReg(src, reg, .{ .immediate = addr });
|
||||
try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr });
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
|
||||
}
|
||||
},
|
||||
@@ -3236,7 +3234,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (!self.wantSafety())
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
|
||||
return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
|
||||
},
|
||||
.immediate => |unsigned_x| {
|
||||
const x = @bitCast(i64, unsigned_x);
|
||||
@@ -3261,7 +3259,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.memory => |addr| {
|
||||
// The value is in memory at a hard-coded address.
|
||||
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||
try self.genSetReg(src, reg, .{ .immediate = addr });
|
||||
try self.genSetReg(src, ty, reg, .{ .immediate = addr });
|
||||
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
|
||||
// LOAD imm=[i12 offset = 0], rs1 =
|
||||
@@ -3280,10 +3278,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
switch (reg.size()) {
|
||||
8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
|
||||
16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
|
||||
32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
|
||||
64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
|
||||
8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }),
|
||||
16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }),
|
||||
32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
|
||||
64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
@@ -3497,7 +3495,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
assert(id3 != 4 and id3 != 5);
|
||||
|
||||
// Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
|
||||
try self.genSetReg(src, reg, MCValue{ .immediate = x });
|
||||
try self.genSetReg(src, ty, reg, MCValue{ .immediate = x });
|
||||
|
||||
// Now, the register contains the address of the value to load into it
|
||||
// Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
|
||||
@@ -3596,7 +3594,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// This immediate is unsigned.
|
||||
const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
|
||||
if (imm >= math.maxInt(U)) {
|
||||
return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
|
||||
return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) };
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
@@ -3710,17 +3708,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
for (param_types) |ty, i| {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Bool, .Int => {
|
||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||
if (next_int_reg >= c_abi_int_param_regs.len) {
|
||||
result.args[i] = .{ .stack_offset = next_stack_offset };
|
||||
next_stack_offset += param_size;
|
||||
if (!ty.hasCodeGenBits()) {
|
||||
assert(cc != .C);
|
||||
result.args[i] = .{ .none = {} };
|
||||
} else {
|
||||
const aliased_reg = registerAlias(
|
||||
c_abi_int_param_regs[next_int_reg],
|
||||
param_size,
|
||||
);
|
||||
result.args[i] = .{ .register = aliased_reg };
|
||||
next_int_reg += 1;
|
||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||
if (next_int_reg >= c_abi_int_param_regs.len) {
|
||||
result.args[i] = .{ .stack_offset = next_stack_offset };
|
||||
next_stack_offset += param_size;
|
||||
} else {
|
||||
const aliased_reg = registerAlias(
|
||||
c_abi_int_param_regs[next_int_reg],
|
||||
param_size,
|
||||
);
|
||||
result.args[i] = .{ .register = aliased_reg };
|
||||
next_int_reg += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}),
|
||||
|
||||
@@ -161,15 +161,19 @@ pub const Context = struct {
|
||||
pub fn gen(self: *Context) InnerError!void {
|
||||
assert(self.code.items.len == 0);
|
||||
try self.genFunctype();
|
||||
const writer = self.code.writer();
|
||||
|
||||
// Reserve space to write the size after generating the code as well as space for locals count
|
||||
try self.code.resize(10);
|
||||
|
||||
// Write instructions
|
||||
// TODO: check for and handle death of instructions
|
||||
const tv = self.decl.typed_value.most_recent.typed_value;
|
||||
const mod_fn = tv.val.castTag(.function).?.data;
|
||||
const mod_fn = blk: {
|
||||
if (tv.val.castTag(.function)) |func| break :blk func.data;
|
||||
if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
|
||||
return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
|
||||
};
|
||||
|
||||
// Reserve space to write the size after generating the code as well as space for locals count
|
||||
try self.code.resize(10);
|
||||
|
||||
try self.genBody(mod_fn.body);
|
||||
|
||||
// finally, write our local types at the 'offset' position
|
||||
@@ -189,6 +193,7 @@ pub const Context = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
|
||||
// Fill in the size of the generated code to the reserved space at the
|
||||
@@ -239,9 +244,16 @@ pub const Context = struct {
|
||||
|
||||
fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
|
||||
const func_inst = inst.func.castTag(.constant).?;
|
||||
const func = func_inst.val.castTag(.function).?.data;
|
||||
const target = func.owner_decl;
|
||||
const target_ty = target.typed_value.most_recent.typed_value.ty;
|
||||
const func_val = inst.func.value().?;
|
||||
|
||||
const target = blk: {
|
||||
if (func_val.castTag(.function)) |func| {
|
||||
break :blk func.data.owner_decl;
|
||||
} else if (func_val.castTag(.extern_fn)) |ext_fn| {
|
||||
break :blk ext_fn.data;
|
||||
}
|
||||
return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()});
|
||||
};
|
||||
|
||||
for (inst.args) |arg| {
|
||||
const arg_val = self.resolveInst(arg);
|
||||
@@ -495,7 +507,7 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
|
||||
// of operand has codegen bits we should break with a value
|
||||
// if operand has codegen bits we should break with a value
|
||||
if (br.operand.ty.hasCodeGenBits()) {
|
||||
const operand = self.resolveInst(br.operand);
|
||||
try self.emitWValue(operand);
|
||||
|
||||
@@ -33,9 +33,18 @@ base: link.File,
|
||||
|
||||
/// List of all function Decls to be written to the output file. The index of
|
||||
/// each Decl in this list at the time of writing the binary is used as the
|
||||
/// function index.
|
||||
/// function index. In the event where ext_funcs' size is not 0, the index of
|
||||
/// each function is added on top of the ext_funcs' length.
|
||||
/// TODO: can/should we access some data structure in Module directly?
|
||||
funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
|
||||
/// List of all extern function Decls to be written to the `import` section of the
|
||||
/// wasm binary. The positin in the list defines the function index
|
||||
ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
|
||||
/// When importing objects from the host environment, a name must be supplied.
|
||||
/// LLVM uses "env" by default when none is given. This would be a good default for Zig
|
||||
/// to support existing code.
|
||||
/// TODO: Allow setting this through a flag?
|
||||
host_name: []const u8 = "env",
|
||||
|
||||
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
|
||||
assert(options.object_format == .wasm);
|
||||
@@ -76,7 +85,13 @@ pub fn deinit(self: *Wasm) void {
|
||||
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
|
||||
}
|
||||
for (self.ext_funcs.items) |decl| {
|
||||
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
|
||||
}
|
||||
self.funcs.deinit(self.base.allocator);
|
||||
self.ext_funcs.deinit(self.base.allocator);
|
||||
}
|
||||
|
||||
// Generate code for the Decl, storing it in memory to be later written to
|
||||
@@ -85,8 +100,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
const typed_value = decl.typed_value.most_recent.typed_value;
|
||||
if (typed_value.ty.zigTypeTag() != .Fn)
|
||||
return error.TODOImplementNonFnDeclsForWasm;
|
||||
if (typed_value.val.tag() == .extern_fn)
|
||||
return error.TODOImplementExternFnDeclsForWasm;
|
||||
|
||||
if (decl.fn_link.wasm) |*fn_data| {
|
||||
fn_data.functype.items.len = 0;
|
||||
@@ -94,7 +107,12 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
fn_data.idx_refs.items.len = 0;
|
||||
} else {
|
||||
decl.fn_link.wasm = .{};
|
||||
try self.funcs.append(self.base.allocator, decl);
|
||||
// dependent on function type, appends it to the correct list
|
||||
switch (decl.typed_value.most_recent.typed_value.val.tag()) {
|
||||
.function => try self.funcs.append(self.base.allocator, decl),
|
||||
.extern_fn => try self.ext_funcs.append(self.base.allocator, decl),
|
||||
else => return error.TODOImplementNonFnDeclsForWasm,
|
||||
}
|
||||
}
|
||||
const fn_data = &decl.fn_link.wasm.?;
|
||||
|
||||
@@ -143,7 +161,12 @@ pub fn updateDeclExports(
|
||||
pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
|
||||
// TODO: remove this assert when non-function Decls are implemented
|
||||
assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
|
||||
_ = self.funcs.swapRemove(self.getFuncidx(decl).?);
|
||||
const func_idx = self.getFuncidx(decl).?;
|
||||
switch (decl.typed_value.most_recent.typed_value.val.tag()) {
|
||||
.function => _ = self.funcs.swapRemove(func_idx),
|
||||
.extern_fn => _ = self.ext_funcs.swapRemove(func_idx),
|
||||
else => unreachable,
|
||||
}
|
||||
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
|
||||
@@ -172,15 +195,46 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
// Type section
|
||||
{
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
for (self.funcs.items) |decl| {
|
||||
try file.writeAll(decl.fn_link.wasm.?.functype.items);
|
||||
}
|
||||
|
||||
// extern functions are defined in the wasm binary first through the `import`
|
||||
// section, so define their func types first
|
||||
for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items);
|
||||
for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items);
|
||||
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
.type,
|
||||
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
||||
@intCast(u32, self.funcs.items.len),
|
||||
@intCast(u32, self.ext_funcs.items.len + self.funcs.items.len),
|
||||
);
|
||||
}
|
||||
|
||||
// Import section
|
||||
{
|
||||
// TODO: implement non-functions imports
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
const writer = file.writer();
|
||||
for (self.ext_funcs.items) |decl, typeidx| {
|
||||
try leb.writeULEB128(writer, @intCast(u32, self.host_name.len));
|
||||
try writer.writeAll(self.host_name);
|
||||
|
||||
// wasm requires the length of the import name with no null-termination
|
||||
const decl_len = mem.len(decl.name);
|
||||
try leb.writeULEB128(writer, @intCast(u32, decl_len));
|
||||
try writer.writeAll(decl.name[0..decl_len]);
|
||||
|
||||
// emit kind and the function type
|
||||
try writer.writeByte(wasm.externalKind(.function));
|
||||
try leb.writeULEB128(writer, @intCast(u32, typeidx));
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
.import,
|
||||
@intCast(u32, (try file.getPos()) - header_offset - header_size),
|
||||
@intCast(u32, self.ext_funcs.items.len),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -188,7 +242,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
{
|
||||
const header_offset = try reserveVecSectionHeader(file);
|
||||
const writer = file.writer();
|
||||
for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
|
||||
for (self.funcs.items) |_, typeidx| {
|
||||
const func_idx = @intCast(u32, self.getFuncIdxOffset() + typeidx);
|
||||
try leb.writeULEB128(writer, func_idx);
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
@@ -212,7 +270,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
|
||||
.Fn => {
|
||||
// Type of the export
|
||||
try writer.writeByte(0x00);
|
||||
try writer.writeByte(wasm.externalKind(.function));
|
||||
// Exported function index
|
||||
try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
|
||||
},
|
||||
@@ -523,13 +581,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
|
||||
}
|
||||
|
||||
/// Get the current index of a given Decl in the function list
|
||||
/// TODO: we could maintain a hash map to potentially make this
|
||||
/// This will correctly provide the index, regardless whether the function is extern or not
|
||||
/// TODO: we could maintain a hash map to potentially make this simpler
|
||||
fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
|
||||
return for (self.funcs.items) |func, idx| {
|
||||
if (func == decl) break @intCast(u32, idx);
|
||||
var offset: u32 = 0;
|
||||
const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) {
|
||||
.function => blk: {
|
||||
// when the target is a regular function, we have to calculate
|
||||
// the offset of where the index starts
|
||||
offset += self.getFuncIdxOffset();
|
||||
break :blk self.funcs.items;
|
||||
},
|
||||
.extern_fn => self.ext_funcs.items,
|
||||
else => return null,
|
||||
};
|
||||
return for (slice) |func, idx| {
|
||||
if (func == decl) break @intCast(u32, offset + idx);
|
||||
} else null;
|
||||
}
|
||||
|
||||
/// Based on the size of `ext_funcs` returns the
|
||||
/// offset of the function indices
|
||||
fn getFuncIdxOffset(self: Wasm) u32 {
|
||||
return @intCast(u32, self.ext_funcs.items.len);
|
||||
}
|
||||
|
||||
fn reserveVecSectionHeader(file: fs.File) !u64 {
|
||||
// section id + fixed leb contents size + fixed leb vector length
|
||||
const header_size = 1 + 5 + 5;
|
||||
|
||||
@@ -278,7 +278,9 @@ export fn stage2_attach_segfault_handler() void {
|
||||
// ABI warning
|
||||
export fn stage2_progress_create() *std.Progress {
|
||||
const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
|
||||
ptr.* = std.Progress{};
|
||||
// If the terminal is dumb, we dont want to show the user all the
|
||||
// output.
|
||||
ptr.* = std.Progress{ .dont_print_on_dumb = true };
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1447,7 +1447,7 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
tokenize_error(&t, "unterminated string");
|
||||
break;
|
||||
} else if (t.cur_tok->id == TokenIdCharLiteral) {
|
||||
tokenize_error(&t, "unterminated character literal");
|
||||
tokenize_error(&t, "unterminated Unicode code point literal");
|
||||
break;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
@@ -1456,7 +1456,7 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
case TokenizeStateCharLiteral:
|
||||
case TokenizeStateCharLiteralEnd:
|
||||
case TokenizeStateCharLiteralUnicode:
|
||||
tokenize_error(&t, "unterminated character literal");
|
||||
tokenize_error(&t, "unterminated Unicode code point literal");
|
||||
break;
|
||||
case TokenizeStateSymbol:
|
||||
case TokenizeStateZero:
|
||||
|
||||
Reference in New Issue
Block a user