mirror of
https://github.com/ziglang/zig.git
synced 2026-01-02 03:25:01 +00:00
stage2 ARM: implement slice_elem_val for types with size <= 4
This commit is contained in:
parent
4f4f0bc6f0
commit
c0ae9647f9
@ -1159,13 +1159,29 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
|
||||
fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const mcv = try self.resolveInst(ty_op.operand);
|
||||
switch (mcv) {
|
||||
.stack_offset => |off| {
|
||||
break :result MCValue{ .stack_offset = off };
|
||||
},
|
||||
else => return self.fail("TODO implement slice_ptr for {}", .{mcv}),
|
||||
}
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch});
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const mcv = try self.resolveInst(ty_op.operand);
|
||||
switch (mcv) {
|
||||
.stack_offset => |off| {
|
||||
break :result MCValue{ .stack_offset = off + 4 };
|
||||
},
|
||||
else => return self.fail("TODO implement slice_len for {}", .{mcv}),
|
||||
}
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
@ -1184,7 +1200,76 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const is_volatile = false; // TODO
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch});
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
|
||||
const slice_mcv = try self.resolveInst(bin_op.lhs);
|
||||
const index_mcv = try self.resolveInst(bin_op.rhs);
|
||||
|
||||
const slice_ty = self.air.typeOf(bin_op.lhs);
|
||||
const elem_ty = slice_ty.childType();
|
||||
const elem_size = elem_ty.abiSize(self.target.*);
|
||||
|
||||
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
|
||||
|
||||
// TODO optimize this for the case when elem_size is a power
|
||||
// of two (includes elem_size == 1)
|
||||
const offset_mcv = try self.genArmMulConstant(inst, bin_op.rhs, 1, @intCast(u32, elem_size));
|
||||
assert(offset_mcv == .register); // result of multiplication should always be register
|
||||
|
||||
const base_mcv: MCValue = switch (slice_mcv) {
|
||||
.stack_offset => |off| blk: {
|
||||
const reg = try self.register_manager.allocReg(null, &.{offset_mcv.register});
|
||||
try self.genSetReg(slice_ptr_field_type, reg, MCValue{ .stack_offset = off });
|
||||
break :blk MCValue{ .register = reg };
|
||||
},
|
||||
else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
|
||||
};
|
||||
|
||||
if (elem_size <= 4) {
|
||||
const dst_reg = try self.register_manager.allocReg(inst, &.{ base_mcv.register, offset_mcv.register });
|
||||
switch (elem_size) {
|
||||
1, 4 => {
|
||||
const tag: Mir.Inst.Tag = switch (elem_size) {
|
||||
1 => .ldrb,
|
||||
4 => .ldr,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag,
|
||||
.cond = .al,
|
||||
.data = .{ .rr_offset = .{
|
||||
.rt = dst_reg,
|
||||
.rn = base_mcv.register,
|
||||
.offset = .{ .offset = Instruction.Offset.reg(offset_mcv.register, 0) },
|
||||
} },
|
||||
});
|
||||
},
|
||||
2 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldrh,
|
||||
.cond = .al,
|
||||
.data = .{ .rr_extra_offset = .{
|
||||
.rt = dst_reg,
|
||||
.rn = base_mcv.register,
|
||||
.offset = .{ .offset = Instruction.ExtraLoadStoreOffset.reg(offset_mcv.register) },
|
||||
} },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
break :result MCValue{ .register = dst_reg };
|
||||
} else {
|
||||
// const dst_mcv = try self.allocRegOrMem(inst, false);
|
||||
return self.fail("TODO implement slice_elem_val for elem_size >= 4", .{});
|
||||
}
|
||||
|
||||
_ = offset_mcv;
|
||||
_ = slice_mcv;
|
||||
_ = index_mcv;
|
||||
_ = offset_mcv;
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
@ -1839,6 +1924,58 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai
|
||||
return dst_mcv;
|
||||
}
|
||||
|
||||
fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_index: Liveness.OperandInt, imm: u32) !MCValue {
|
||||
const mcv = try self.resolveInst(op);
|
||||
const rhs = MCValue{ .immediate = imm };
|
||||
|
||||
const lhs_is_register = mcv == .register;
|
||||
const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op, op_index, mcv);
|
||||
|
||||
// Destination must be a register
|
||||
// LHS must be a register
|
||||
// RHS must be a register
|
||||
var dst_mcv: MCValue = undefined;
|
||||
var lhs_mcv: MCValue = mcv;
|
||||
var rhs_mcv: MCValue = rhs;
|
||||
|
||||
// Allocate registers for operands and/or destination
|
||||
if (reuse_lhs) {
|
||||
// Allocate 1 register
|
||||
rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(null, &.{mcv.register}) };
|
||||
dst_mcv = mcv;
|
||||
} else {
|
||||
// Allocate 1 or 2 registers
|
||||
if (lhs_is_register) {
|
||||
// Move RHS to register
|
||||
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{mcv.register}) };
|
||||
rhs_mcv = dst_mcv;
|
||||
} else {
|
||||
// Move LHS and RHS to register
|
||||
const regs = try self.register_manager.allocRegs(2, .{ inst, null }, &.{});
|
||||
lhs_mcv = MCValue{ .register = regs[0] };
|
||||
rhs_mcv = MCValue{ .register = regs[1] };
|
||||
dst_mcv = lhs_mcv;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the operands to the newly allocated registers
|
||||
if (!lhs_is_register) {
|
||||
try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, mcv);
|
||||
}
|
||||
try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs);
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mul,
|
||||
.cond = .al,
|
||||
.data = .{ .rrr = .{
|
||||
.rd = dst_mcv.register,
|
||||
.rn = lhs_mcv.register,
|
||||
.rm = rhs_mcv.register,
|
||||
} },
|
||||
});
|
||||
return dst_mcv;
|
||||
}
|
||||
|
||||
fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
|
||||
const ty_str = self.air.instructions.items(.data)[inst].ty_str;
|
||||
const zir = &self.mod_fn.owner_decl.getFileScope().zir;
|
||||
|
||||
174
src/codegen.zig
174
src/codegen.zig
@ -168,9 +168,10 @@ pub fn generateSymbol(
|
||||
),
|
||||
};
|
||||
},
|
||||
.Array => {
|
||||
// TODO populate .debug_info for the array
|
||||
if (typed_value.val.castTag(.bytes)) |payload| {
|
||||
.Array => switch (typed_value.val.tag()) {
|
||||
.bytes => {
|
||||
// TODO populate .debug_info for the array
|
||||
const payload = typed_value.val.castTag(.bytes).?;
|
||||
if (typed_value.ty.sentinel()) |sentinel| {
|
||||
try code.ensureUnusedCapacity(payload.data.len + 1);
|
||||
code.appendSliceAssumeCapacity(payload.data);
|
||||
@ -188,94 +189,83 @@ pub fn generateSymbol(
|
||||
} else {
|
||||
return Result{ .externally_managed = payload.data };
|
||||
}
|
||||
}
|
||||
return Result{
|
||||
},
|
||||
.array => {
|
||||
// TODO populate .debug_info for the array
|
||||
const elem_vals = typed_value.val.castTag(.array).?.data;
|
||||
const elem_ty = typed_value.ty.elemType();
|
||||
for (elem_vals) |elem_val| {
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = elem_ty,
|
||||
.val = elem_val,
|
||||
}, code, debug_output)) {
|
||||
.appended => {},
|
||||
.externally_managed => |slice| {
|
||||
code.appendSliceAssumeCapacity(slice);
|
||||
return Result{ .appended = {} };
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
}
|
||||
return Result{ .appended = {} };
|
||||
},
|
||||
else => return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for more kinds of arrays",
|
||||
.{},
|
||||
"TODO implement generateSymbol for array type value: {s}",
|
||||
.{@tagName(typed_value.val.tag())},
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
.Pointer => switch (typed_value.ty.ptrSize()) {
|
||||
.Slice => {
|
||||
.Pointer => switch (typed_value.val.tag()) {
|
||||
.variable => {
|
||||
const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
|
||||
return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
|
||||
},
|
||||
.decl_ref => {
|
||||
const decl = typed_value.val.castTag(.decl_ref).?.data;
|
||||
return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
|
||||
},
|
||||
.slice => {
|
||||
// TODO populate .debug_info for the slice
|
||||
const slice = typed_value.val.castTag(.slice).?.data;
|
||||
|
||||
// generate ptr
|
||||
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = slice_ptr_field_type,
|
||||
.val = typed_value.val.slicePtr(),
|
||||
.val = slice.ptr,
|
||||
}, code, debug_output)) {
|
||||
.appended => {},
|
||||
.externally_managed => |slice| {
|
||||
code.appendSliceAssumeCapacity(slice);
|
||||
.externally_managed => |external_slice| {
|
||||
code.appendSliceAssumeCapacity(external_slice);
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
|
||||
// generate length
|
||||
var int_buffer: Value.Payload.U64 = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
.data = typed_value.val.sliceLen(),
|
||||
};
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = Type.initTag(.usize),
|
||||
.val = Value.initPayload(&int_buffer.base),
|
||||
.val = slice.len,
|
||||
}, code, debug_output)) {
|
||||
.appended => {},
|
||||
.externally_managed => |slice| {
|
||||
code.appendSliceAssumeCapacity(slice);
|
||||
.externally_managed => |external_slice| {
|
||||
code.appendSliceAssumeCapacity(external_slice);
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for slice {}",
|
||||
.{typed_value.val},
|
||||
),
|
||||
};
|
||||
return Result{ .appended = {} };
|
||||
},
|
||||
else => {
|
||||
// TODO populate .debug_info for the pointer
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
const decl = payload.data;
|
||||
if (decl.analysis != .complete) return error.AnalysisFail;
|
||||
decl.alive = true;
|
||||
// TODO handle the dependency of this symbol on the decl's vaddr.
|
||||
// If the decl changes vaddr, then this symbol needs to get regenerated.
|
||||
const vaddr = bin_file.getDeclVAddr(decl);
|
||||
const endian = bin_file.options.target.cpu.arch.endian();
|
||||
switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
|
||||
16 => {
|
||||
try code.resize(2);
|
||||
mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
|
||||
},
|
||||
32 => {
|
||||
try code.resize(4);
|
||||
mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
|
||||
},
|
||||
64 => {
|
||||
try code.resize(8);
|
||||
mem.writeInt(u64, code.items[0..8], vaddr, endian);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for pointer {}",
|
||||
.{typed_value.val},
|
||||
),
|
||||
};
|
||||
else => return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for pointer type value: '{s}'",
|
||||
.{@tagName(typed_value.val.tag())},
|
||||
),
|
||||
},
|
||||
},
|
||||
.Int => {
|
||||
@ -401,3 +391,61 @@ pub fn generateSymbol(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerDeclRef(
|
||||
bin_file: *link.File,
|
||||
src_loc: Module.SrcLoc,
|
||||
typed_value: TypedValue,
|
||||
decl: *Module.Decl,
|
||||
code: *std.ArrayList(u8),
|
||||
debug_output: DebugInfoOutput,
|
||||
) GenerateSymbolError!Result {
|
||||
if (typed_value.ty.isSlice()) {
|
||||
// generate ptr
|
||||
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = slice_ptr_field_type,
|
||||
.val = typed_value.val,
|
||||
}, code, debug_output)) {
|
||||
.appended => {},
|
||||
.externally_managed => |external_slice| {
|
||||
code.appendSliceAssumeCapacity(external_slice);
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
|
||||
// generate length
|
||||
var slice_len: Value.Payload.U64 = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
.data = typed_value.val.sliceLen(),
|
||||
};
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = Type.initTag(.usize),
|
||||
.val = Value.initPayload(&slice_len.base),
|
||||
}, code, debug_output)) {
|
||||
.appended => {},
|
||||
.externally_managed => |external_slice| {
|
||||
code.appendSliceAssumeCapacity(external_slice);
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
|
||||
if (decl.analysis != .complete) return error.AnalysisFail;
|
||||
decl.alive = true;
|
||||
// TODO handle the dependency of this symbol on the decl's vaddr.
|
||||
// If the decl changes vaddr, then this symbol needs to get regenerated.
|
||||
const vaddr = bin_file.getDeclVAddr(decl);
|
||||
const endian = bin_file.options.target.cpu.arch.endian();
|
||||
switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
|
||||
16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
|
||||
32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, vaddr), endian),
|
||||
64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
|
||||
@ -637,4 +637,25 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("slices", linux_arm);
|
||||
case.addCompareOutput(
|
||||
\\var array = [_]u32{ 0, 42, 123, 69 };
|
||||
\\var s: []const u32 = &array;
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ assert(s[0] == 0);
|
||||
\\ assert(s[1] == 42);
|
||||
\\ assert(s[2] == 123);
|
||||
\\ assert(s[3] == 69);
|
||||
\\}
|
||||
\\
|
||||
\\fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user