mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 19:23:08 +00:00
Merge pull request #10832 from ziglang/x64-more-structs
stage2,x64: pass more tests with structs
This commit is contained in:
commit
2302ded951
@ -2036,16 +2036,60 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
const mcv = try self.resolveInst(operand);
|
const mcv = try self.resolveInst(operand);
|
||||||
const struct_ty = self.air.typeOf(operand);
|
const struct_ty = self.air.typeOf(operand);
|
||||||
const struct_size = @intCast(i32, struct_ty.abiSize(self.target.*));
|
const struct_size = struct_ty.abiSize(self.target.*);
|
||||||
const struct_field_offset = @intCast(i32, struct_ty.structFieldOffset(index, self.target.*));
|
const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);
|
||||||
const struct_field_ty = struct_ty.structFieldType(index);
|
const struct_field_ty = struct_ty.structFieldType(index);
|
||||||
const struct_field_size = @intCast(i32, struct_field_ty.abiSize(self.target.*));
|
const struct_field_size = struct_field_ty.abiSize(self.target.*);
|
||||||
|
|
||||||
switch (mcv) {
|
switch (mcv) {
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const stack_offset = off + struct_size - struct_field_offset - struct_field_size;
|
const offset_to_field = struct_size - struct_field_offset - struct_field_size;
|
||||||
|
const stack_offset = off + @intCast(i32, offset_to_field);
|
||||||
break :result MCValue{ .stack_offset = stack_offset };
|
break :result MCValue{ .stack_offset = stack_offset };
|
||||||
},
|
},
|
||||||
|
.register => |reg| {
|
||||||
|
self.register_manager.freezeRegs(&.{reg});
|
||||||
|
defer self.register_manager.unfreezeRegs(&.{reg});
|
||||||
|
|
||||||
|
const dst_mcv = blk: {
|
||||||
|
if (self.reuseOperand(inst, operand, 0, mcv)) {
|
||||||
|
break :blk mcv;
|
||||||
|
} else {
|
||||||
|
const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() });
|
||||||
|
break :blk dst_mcv;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shift by struct_field_offset.
|
||||||
|
const shift_amount = @intCast(u8, struct_field_offset * 8);
|
||||||
|
if (shift_amount > 0) {
|
||||||
|
if (shift_amount == 1) {
|
||||||
|
_ = try self.addInst(.{
|
||||||
|
.tag = .shr,
|
||||||
|
.ops = (Mir.Ops{
|
||||||
|
.reg1 = dst_mcv.register,
|
||||||
|
}).encode(),
|
||||||
|
.data = undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_ = try self.addInst(.{
|
||||||
|
.tag = .shr,
|
||||||
|
.ops = (Mir.Ops{
|
||||||
|
.reg1 = dst_mcv.register,
|
||||||
|
.flags = 0b10,
|
||||||
|
}).encode(),
|
||||||
|
.data = .{ .imm = shift_amount },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask with reg.size() - struct_field_size
|
||||||
|
const mask_shift = @intCast(u6, (64 - struct_field_ty.bitSize(self.target.*)));
|
||||||
|
const mask = (~@as(u64, 0)) >> mask_shift;
|
||||||
|
try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask });
|
||||||
|
|
||||||
|
break :result dst_mcv;
|
||||||
|
},
|
||||||
else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
|
else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -3498,6 +3542,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void {
|
fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void {
|
||||||
|
const abi_size = ty.abiSize(self.target.*);
|
||||||
switch (mcv) {
|
switch (mcv) {
|
||||||
.dead => unreachable,
|
.dead => unreachable,
|
||||||
.ptr_embedded_in_code => unreachable,
|
.ptr_embedded_in_code => unreachable,
|
||||||
@ -3521,7 +3566,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
|
|||||||
return self.genSetStack(ty, stack_offset, .{ .register = reg });
|
return self.genSetStack(ty, stack_offset, .{ .register = reg });
|
||||||
},
|
},
|
||||||
.immediate => |x_big| {
|
.immediate => |x_big| {
|
||||||
const abi_size = ty.abiSize(self.target.*);
|
|
||||||
const adj_off = stack_offset + @intCast(i32, abi_size);
|
const adj_off = stack_offset + @intCast(i32, abi_size);
|
||||||
if (adj_off > 128) {
|
if (adj_off > 128) {
|
||||||
return self.fail("TODO implement set stack variable with large stack offset", .{});
|
return self.fail("TODO implement set stack variable with large stack offset", .{});
|
||||||
@ -3594,7 +3638,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
|
|||||||
if (stack_offset > math.maxInt(i32)) {
|
if (stack_offset > math.maxInt(i32)) {
|
||||||
return self.fail("stack offset too large", .{});
|
return self.fail("stack offset too large", .{});
|
||||||
}
|
}
|
||||||
const abi_size = ty.abiSize(self.target.*);
|
|
||||||
const adj_off = stack_offset + @intCast(i32, abi_size);
|
const adj_off = stack_offset + @intCast(i32, abi_size);
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .mov,
|
.tag = .mov,
|
||||||
@ -3611,12 +3654,47 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
|
|||||||
.got_load,
|
.got_load,
|
||||||
.direct_load,
|
.direct_load,
|
||||||
=> {
|
=> {
|
||||||
if (ty.abiSize(self.target.*) <= 8) {
|
if (abi_size <= 8) {
|
||||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try self.register_manager.getReg(.rax, null);
|
||||||
|
try self.register_manager.getReg(.rcx, null);
|
||||||
|
|
||||||
|
self.register_manager.freezeRegs(&.{ .rax, .rcx, .rbp });
|
||||||
|
defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rbp });
|
||||||
|
|
||||||
|
const addr_reg: Register = blk: {
|
||||||
|
switch (mcv) {
|
||||||
|
.memory => |addr| {
|
||||||
|
const reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr });
|
||||||
|
break :blk reg;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
return self.fail("TODO implement memcpy for setting stack from {}", .{mcv});
|
return self.fail("TODO implement memcpy for setting stack from {}", .{mcv});
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.register_manager.freezeRegs(&.{addr_reg});
|
||||||
|
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||||
|
|
||||||
|
const regs = try self.register_manager.allocRegs(2, .{ null, null });
|
||||||
|
const count_reg = regs[0];
|
||||||
|
const tmp_reg = regs[1];
|
||||||
|
|
||||||
|
// TODO allow for abi_size to be u64
|
||||||
|
try self.genSetReg(Type.u32, count_reg, .{ .immediate = @intCast(u32, abi_size) });
|
||||||
|
|
||||||
|
return self.genInlineMemcpy(
|
||||||
|
-(stack_offset + @intCast(i32, abi_size)),
|
||||||
|
.rbp,
|
||||||
|
addr_reg.to64(),
|
||||||
|
count_reg.to64(),
|
||||||
|
tmp_reg.to8(),
|
||||||
|
);
|
||||||
|
},
|
||||||
.ptr_stack_offset => {
|
.ptr_stack_offset => {
|
||||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||||
@ -3627,7 +3705,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const abi_size = ty.abiSize(self.target.*);
|
|
||||||
if (abi_size <= 8) {
|
if (abi_size <= 8) {
|
||||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||||
@ -4670,11 +4747,17 @@ fn parseRegName(name: []const u8) ?Register {
|
|||||||
|
|
||||||
fn registerAlias(reg: Register, size_bytes: u32) Register {
|
fn registerAlias(reg: Register, size_bytes: u32) Register {
|
||||||
// For x86_64 we have to pick a smaller register alias depending on abi size.
|
// For x86_64 we have to pick a smaller register alias depending on abi size.
|
||||||
switch (size_bytes) {
|
if (size_bytes == 0) {
|
||||||
1 => return reg.to8(),
|
unreachable; // should be comptime known
|
||||||
2 => return reg.to16(),
|
} else if (size_bytes <= 1) {
|
||||||
4 => return reg.to32(),
|
return reg.to8();
|
||||||
8 => return reg.to64(),
|
} else if (size_bytes <= 2) {
|
||||||
else => unreachable,
|
return reg.to16();
|
||||||
|
} else if (size_bytes <= 4) {
|
||||||
|
return reg.to32();
|
||||||
|
} else if (size_bytes <= 8) {
|
||||||
|
return reg.to64();
|
||||||
|
} else {
|
||||||
|
unreachable; // TODO handle floating-point registers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,6 +133,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
|
|||||||
.lea => try emit.mirLea(inst),
|
.lea => try emit.mirLea(inst),
|
||||||
.lea_pie => try emit.mirLeaPie(inst),
|
.lea_pie => try emit.mirLeaPie(inst),
|
||||||
|
|
||||||
|
.shl => try emit.mirShift(.shl, inst),
|
||||||
|
.sal => try emit.mirShift(.sal, inst),
|
||||||
|
.shr => try emit.mirShift(.shr, inst),
|
||||||
|
.sar => try emit.mirShift(.sar, inst),
|
||||||
|
|
||||||
.imul_complex => try emit.mirIMulComplex(inst),
|
.imul_complex => try emit.mirIMulComplex(inst),
|
||||||
|
|
||||||
.push => try emit.mirPushPop(.push, inst),
|
.push => try emit.mirPushPop(.push, inst),
|
||||||
@ -653,6 +658,31 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
|||||||
return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
|
return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
|
||||||
|
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
|
||||||
|
switch (ops.flags) {
|
||||||
|
0b00 => {
|
||||||
|
// sal reg1, 1
|
||||||
|
// M1
|
||||||
|
return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
|
||||||
|
},
|
||||||
|
0b01 => {
|
||||||
|
// sal reg1, .cl
|
||||||
|
// MC
|
||||||
|
return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
|
||||||
|
},
|
||||||
|
0b10 => {
|
||||||
|
// sal reg1, imm8
|
||||||
|
// MI
|
||||||
|
const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm);
|
||||||
|
return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
|
||||||
|
},
|
||||||
|
0b11 => {
|
||||||
|
return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||||
assert(tag == .imul_complex);
|
assert(tag == .imul_complex);
|
||||||
@ -743,13 +773,13 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
|||||||
emit.code,
|
emit.code,
|
||||||
);
|
);
|
||||||
const end_offset = emit.code.items.len;
|
const end_offset = emit.code.items.len;
|
||||||
|
const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index;
|
||||||
|
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||||
const reloc_type = switch (ops.flags) {
|
const reloc_type = switch (ops.flags) {
|
||||||
0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
|
0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
|
||||||
0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
|
0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
|
||||||
else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
|
else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
|
||||||
};
|
};
|
||||||
const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index;
|
|
||||||
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
|
||||||
const decl = macho_file.active_decl.?;
|
const decl = macho_file.active_decl.?;
|
||||||
try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
|
try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
|
||||||
.offset = @intCast(u32, end_offset - 4),
|
.offset = @intCast(u32, end_offset - 4),
|
||||||
@ -1064,6 +1094,10 @@ const Tag = enum {
|
|||||||
setng,
|
setng,
|
||||||
setnle,
|
setnle,
|
||||||
setg,
|
setg,
|
||||||
|
shl,
|
||||||
|
sal,
|
||||||
|
shr,
|
||||||
|
sar,
|
||||||
|
|
||||||
fn isSetCC(tag: Tag) bool {
|
fn isSetCC(tag: Tag) bool {
|
||||||
return switch (tag) {
|
return switch (tag) {
|
||||||
@ -1119,9 +1153,18 @@ const Encoding = enum {
|
|||||||
/// OP imm32
|
/// OP imm32
|
||||||
i,
|
i,
|
||||||
|
|
||||||
|
/// OP r/m64, 1
|
||||||
|
m1,
|
||||||
|
|
||||||
|
/// OP r/m64, .cl
|
||||||
|
mc,
|
||||||
|
|
||||||
/// OP r/m64, imm32
|
/// OP r/m64, imm32
|
||||||
mi,
|
mi,
|
||||||
|
|
||||||
|
/// OP r/m64, imm8
|
||||||
|
mi8,
|
||||||
|
|
||||||
/// OP r/m64, r64
|
/// OP r/m64, r64
|
||||||
mr,
|
mr,
|
||||||
|
|
||||||
@ -1230,12 +1273,25 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
|
|||||||
.ret_far => OpCode.oneByte(0xca),
|
.ret_far => OpCode.oneByte(0xca),
|
||||||
else => null,
|
else => null,
|
||||||
},
|
},
|
||||||
|
.m1 => return switch (tag) {
|
||||||
|
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1),
|
||||||
|
else => null,
|
||||||
|
},
|
||||||
|
.mc => return switch (tag) {
|
||||||
|
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3),
|
||||||
|
else => null,
|
||||||
|
},
|
||||||
.mi => return switch (tag) {
|
.mi => return switch (tag) {
|
||||||
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
|
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
|
||||||
.mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
|
.mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
|
||||||
.@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
|
.@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
|
||||||
else => null,
|
else => null,
|
||||||
},
|
},
|
||||||
|
.mi8 => return switch (tag) {
|
||||||
|
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83),
|
||||||
|
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1),
|
||||||
|
else => null,
|
||||||
|
},
|
||||||
.mr => return switch (tag) {
|
.mr => return switch (tag) {
|
||||||
.adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
|
.adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
|
||||||
.add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),
|
.add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),
|
||||||
@ -1331,6 +1387,11 @@ inline fn getModRmExt(tag: Tag) ?u3 {
|
|||||||
.setnle,
|
.setnle,
|
||||||
.setg,
|
.setg,
|
||||||
=> 0x0,
|
=> 0x0,
|
||||||
|
.shl,
|
||||||
|
.sal,
|
||||||
|
=> 0x4,
|
||||||
|
.shr => 0x5,
|
||||||
|
.sar => 0x7,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1528,8 +1589,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
|
|||||||
encoder.imm32(@bitCast(i32, imm));
|
encoder.imm32(@bitCast(i32, imm));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
|
fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
const opc = getOpCode(tag, .m, false).?;
|
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
|
||||||
const modrm_ext = getModRmExt(tag).?;
|
const modrm_ext = getModRmExt(tag).?;
|
||||||
switch (reg_or_mem) {
|
switch (reg_or_mem) {
|
||||||
.register => |reg| {
|
.register => |reg| {
|
||||||
@ -1537,11 +1598,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
|
|||||||
if (reg.size() == 16) {
|
if (reg.size() == 16) {
|
||||||
encoder.prefix16BitMode();
|
encoder.prefix16BitMode();
|
||||||
}
|
}
|
||||||
|
const wide = if (tag == .jmp_near) false else setRexWRegister(reg);
|
||||||
encoder.rex(.{
|
encoder.rex(.{
|
||||||
.w = switch (reg) {
|
.w = wide,
|
||||||
.ah, .bh, .ch, .dh => true,
|
|
||||||
else => false,
|
|
||||||
},
|
|
||||||
.b = reg.isExtended(),
|
.b = reg.isExtended(),
|
||||||
});
|
});
|
||||||
opc.encode(encoder);
|
opc.encode(encoder);
|
||||||
@ -1553,8 +1612,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
|
|||||||
encoder.prefix16BitMode();
|
encoder.prefix16BitMode();
|
||||||
}
|
}
|
||||||
if (mem_op.base) |base| {
|
if (mem_op.base) |base| {
|
||||||
|
const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr;
|
||||||
encoder.rex(.{
|
encoder.rex(.{
|
||||||
.w = false,
|
.w = wide,
|
||||||
.b = base.isExtended(),
|
.b = base.isExtended(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1564,6 +1624,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
|
return lowerToMxEnc(tag, reg_or_mem, .m, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
|
return lowerToMxEnc(tag, reg_or_mem, .m1, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
|
return lowerToMxEnc(tag, reg_or_mem, .mc, code);
|
||||||
|
}
|
||||||
|
|
||||||
fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
|
fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
return lowerToTdFdEnc(tag, reg, moffs, code, true);
|
return lowerToTdFdEnc(tag, reg, moffs, code, true);
|
||||||
}
|
}
|
||||||
@ -1614,9 +1686,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Inn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
|
fn lowerToMiXEnc(
|
||||||
|
tag: Tag,
|
||||||
|
reg_or_mem: RegisterOrMemory,
|
||||||
|
imm: u32,
|
||||||
|
enc: Encoding,
|
||||||
|
code: *std.ArrayList(u8),
|
||||||
|
) InnerError!void {
|
||||||
const modrm_ext = getModRmExt(tag).?;
|
const modrm_ext = getModRmExt(tag).?;
|
||||||
const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?;
|
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
|
||||||
switch (reg_or_mem) {
|
switch (reg_or_mem) {
|
||||||
.register => |dst_reg| {
|
.register => |dst_reg| {
|
||||||
const encoder = try Encoder.init(code, 7);
|
const encoder = try Encoder.init(code, 7);
|
||||||
@ -1632,7 +1710,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr
|
|||||||
});
|
});
|
||||||
opc.encode(encoder);
|
opc.encode(encoder);
|
||||||
encoder.modRm_direct(modrm_ext, dst_reg.lowId());
|
encoder.modRm_direct(modrm_ext, dst_reg.lowId());
|
||||||
encodeImm(encoder, imm, dst_reg.size());
|
encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size());
|
||||||
},
|
},
|
||||||
.memory => |dst_mem| {
|
.memory => |dst_mem| {
|
||||||
const encoder = try Encoder.init(code, 12);
|
const encoder = try Encoder.init(code, 12);
|
||||||
@ -1651,11 +1729,19 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr
|
|||||||
}
|
}
|
||||||
opc.encode(encoder);
|
opc.encode(encoder);
|
||||||
dst_mem.encode(encoder, modrm_ext);
|
dst_mem.encode(encoder, modrm_ext);
|
||||||
encodeImm(encoder, imm, dst_mem.ptr_size.size());
|
encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
|
return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
|
||||||
|
return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code);
|
||||||
|
}
|
||||||
|
|
||||||
fn lowerToRmEnc(
|
fn lowerToRmEnc(
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
reg: Register,
|
reg: Register,
|
||||||
@ -1902,6 +1988,9 @@ test "lower MI encoding" {
|
|||||||
emit.lowered(),
|
emit.lowered(),
|
||||||
"mov qword ptr [rcx*2 + 0x10000000], 0x10",
|
"mov qword ptr [rcx*2 + 0x10000000], 0x10",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code());
|
||||||
|
try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "lower RM encoding" {
|
test "lower RM encoding" {
|
||||||
@ -2100,6 +2189,41 @@ test "lower M encoding" {
|
|||||||
try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");
|
try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "lower M1 and MC encodings" {
|
||||||
|
var emit = TestEmit.init();
|
||||||
|
defer emit.deinit();
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code());
|
||||||
|
try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code());
|
||||||
|
try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code());
|
||||||
|
try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code());
|
||||||
|
try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code());
|
||||||
|
try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code());
|
||||||
|
try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{
|
||||||
|
.disp = @bitCast(u32, @as(i32, -0x10)),
|
||||||
|
.base = .rbp,
|
||||||
|
}), emit.code());
|
||||||
|
try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1");
|
||||||
|
try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{
|
||||||
|
.disp = @bitCast(u32, @as(i32, -0x10)),
|
||||||
|
.base = .rbp,
|
||||||
|
}), emit.code());
|
||||||
|
try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1");
|
||||||
|
|
||||||
|
try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code());
|
||||||
|
try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl");
|
||||||
|
try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code());
|
||||||
|
try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl");
|
||||||
|
|
||||||
|
try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code());
|
||||||
|
try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl");
|
||||||
|
}
|
||||||
|
|
||||||
test "lower O encoding" {
|
test "lower O encoding" {
|
||||||
var emit = TestEmit.init();
|
var emit = TestEmit.init();
|
||||||
defer emit.deinit();
|
defer emit.deinit();
|
||||||
|
|||||||
@ -142,30 +142,6 @@ pub const Inst = struct {
|
|||||||
rcr_scale_dst,
|
rcr_scale_dst,
|
||||||
rcr_scale_imm,
|
rcr_scale_imm,
|
||||||
rcr_mem_index_imm,
|
rcr_mem_index_imm,
|
||||||
shl,
|
|
||||||
shl_mem_imm,
|
|
||||||
shl_scale_src,
|
|
||||||
shl_scale_dst,
|
|
||||||
shl_scale_imm,
|
|
||||||
shl_mem_index_imm,
|
|
||||||
sal,
|
|
||||||
sal_mem_imm,
|
|
||||||
sal_scale_src,
|
|
||||||
sal_scale_dst,
|
|
||||||
sal_scale_imm,
|
|
||||||
sal_mem_index_imm,
|
|
||||||
shr,
|
|
||||||
shr_mem_imm,
|
|
||||||
shr_scale_src,
|
|
||||||
shr_scale_dst,
|
|
||||||
shr_scale_imm,
|
|
||||||
shr_mem_index_imm,
|
|
||||||
sar,
|
|
||||||
sar_mem_imm,
|
|
||||||
sar_scale_src,
|
|
||||||
sar_scale_dst,
|
|
||||||
sar_scale_imm,
|
|
||||||
sar_mem_index_imm,
|
|
||||||
sbb,
|
sbb,
|
||||||
sbb_mem_imm,
|
sbb_mem_imm,
|
||||||
sbb_scale_src,
|
sbb_scale_src,
|
||||||
@ -212,6 +188,37 @@ pub const Inst = struct {
|
|||||||
/// * `Data` contains `linker_sym_index`
|
/// * `Data` contains `linker_sym_index`
|
||||||
lea_pie,
|
lea_pie,
|
||||||
|
|
||||||
|
/// ops flags: form:
|
||||||
|
/// 0b00 reg1, 1
|
||||||
|
/// 0b01 reg1, .cl
|
||||||
|
/// 0b10 reg1, imm8
|
||||||
|
/// Notes:
|
||||||
|
/// * If flags == 0b10, uses `imm`.
|
||||||
|
shl,
|
||||||
|
shl_mem_imm,
|
||||||
|
shl_scale_src,
|
||||||
|
shl_scale_dst,
|
||||||
|
shl_scale_imm,
|
||||||
|
shl_mem_index_imm,
|
||||||
|
sal,
|
||||||
|
sal_mem_imm,
|
||||||
|
sal_scale_src,
|
||||||
|
sal_scale_dst,
|
||||||
|
sal_scale_imm,
|
||||||
|
sal_mem_index_imm,
|
||||||
|
shr,
|
||||||
|
shr_mem_imm,
|
||||||
|
shr_scale_src,
|
||||||
|
shr_scale_dst,
|
||||||
|
shr_scale_imm,
|
||||||
|
shr_mem_index_imm,
|
||||||
|
sar,
|
||||||
|
sar_mem_imm,
|
||||||
|
sar_scale_src,
|
||||||
|
sar_scale_dst,
|
||||||
|
sar_scale_imm,
|
||||||
|
sar_mem_index_imm,
|
||||||
|
|
||||||
/// ops flags: form:
|
/// ops flags: form:
|
||||||
/// 0bX0 reg1
|
/// 0bX0 reg1
|
||||||
/// 0bX1 [reg1 + imm32]
|
/// 0bX1 [reg1 + imm32]
|
||||||
|
|||||||
@ -372,7 +372,6 @@ pub fn generateSymbol(
|
|||||||
return Result{ .appended = {} };
|
return Result{ .appended = {} };
|
||||||
},
|
},
|
||||||
.Struct => {
|
.Struct => {
|
||||||
// TODO debug info
|
|
||||||
const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
|
const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
|
||||||
if (struct_obj.layout == .Packed) {
|
if (struct_obj.layout == .Packed) {
|
||||||
return Result{
|
return Result{
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const maxInt = std.math.maxInt;
|
|||||||
top_level_field: i32,
|
top_level_field: i32,
|
||||||
|
|
||||||
test "top level fields" {
|
test "top level fields" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
|
|
||||||
var instance = @This(){
|
var instance = @This(){
|
||||||
.top_level_field = 1234,
|
.top_level_field = 1234,
|
||||||
@ -176,7 +176,7 @@ const MemberFnTestFoo = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "call member function directly" {
|
test "call member function directly" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
|
|
||||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||||
const result = MemberFnTestFoo.member(instance);
|
const result = MemberFnTestFoo.member(instance);
|
||||||
@ -184,7 +184,7 @@ test "call member function directly" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "store member function in variable" {
|
test "store member function in variable" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
|
|
||||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||||
const memberFn = MemberFnTestFoo.member;
|
const memberFn = MemberFnTestFoo.member;
|
||||||
@ -206,7 +206,7 @@ const MemberFnRand = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test "return struct byval from function" {
|
test "return struct byval from function" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
|
|
||||||
const bar = makeBar2(1234, 5678);
|
const bar = makeBar2(1234, 5678);
|
||||||
try expect(bar.y == 5678);
|
try expect(bar.y == 5678);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user