authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-12-28 22:00:14+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-28 20:38:37-05:00
logc0ae9647f9656ea47c49ffd64443b7da73aeffc7
tree3f39f2283482c3dd05b44404d967475fb495df4c
parent4f4f0bc6f03a84597247504a5ee15ed7f1e050d1

stage2 ARM: implement slice_elem_val for types with size <= 4


3 files changed, 272 insertions(+), 66 deletions(-)

src/arch/arm/CodeGen.zig+140-3
......@@ -1159,13 +1159,29 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
11591159
11601160fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
11611161 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1162 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
1162 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1163 const mcv = try self.resolveInst(ty_op.operand);
1164 switch (mcv) {
1165 .stack_offset => |off| {
1166 break :result MCValue{ .stack_offset = off };
1167 },
1168 else => return self.fail("TODO implement slice_ptr for {}", .{mcv}),
1169 }
1170 };
11631171 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11641172}
11651173
11661174fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
11671175 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch});
1176 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1177 const mcv = try self.resolveInst(ty_op.operand);
1178 switch (mcv) {
1179 .stack_offset => |off| {
1180 break :result MCValue{ .stack_offset = off + 4 };
1181 },
1182 else => return self.fail("TODO implement slice_len for {}", .{mcv}),
1183 }
1184 };
11691185 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11701186}
11711187
......@@ -1184,7 +1200,76 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
11841200fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
11851201 const is_volatile = false; // TODO
11861202 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1187 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});
1203 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
1204 const slice_mcv = try self.resolveInst(bin_op.lhs);
1205 const index_mcv = try self.resolveInst(bin_op.rhs);
1206
1207 const slice_ty = self.air.typeOf(bin_op.lhs);
1208 const elem_ty = slice_ty.childType();
1209 const elem_size = elem_ty.abiSize(self.target.*);
1210
1211 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1212 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
1213
1214 // TODO optimize this for the case when elem_size is a power
1215 // of two (includes elem_size == 1)
1216 const offset_mcv = try self.genArmMulConstant(inst, bin_op.rhs, 1, @intCast(u32, elem_size));
1217 assert(offset_mcv == .register); // result of multiplication should always be register
1218
1219 const base_mcv: MCValue = switch (slice_mcv) {
1220 .stack_offset => |off| blk: {
1221 const reg = try self.register_manager.allocReg(null, &.{offset_mcv.register});
1222 try self.genSetReg(slice_ptr_field_type, reg, MCValue{ .stack_offset = off });
1223 break :blk MCValue{ .register = reg };
1224 },
1225 else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
1226 };
1227
1228 if (elem_size <= 4) {
1229 const dst_reg = try self.register_manager.allocReg(inst, &.{ base_mcv.register, offset_mcv.register });
1230 switch (elem_size) {
1231 1, 4 => {
1232 const tag: Mir.Inst.Tag = switch (elem_size) {
1233 1 => .ldrb,
1234 4 => .ldr,
1235 else => unreachable,
1236 };
1237
1238 _ = try self.addInst(.{
1239 .tag = tag,
1240 .cond = .al,
1241 .data = .{ .rr_offset = .{
1242 .rt = dst_reg,
1243 .rn = base_mcv.register,
1244 .offset = .{ .offset = Instruction.Offset.reg(offset_mcv.register, 0) },
1245 } },
1246 });
1247 },
1248 2 => {
1249 _ = try self.addInst(.{
1250 .tag = .ldrh,
1251 .cond = .al,
1252 .data = .{ .rr_extra_offset = .{
1253 .rt = dst_reg,
1254 .rn = base_mcv.register,
1255 .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.reg(offset_mcv.register) },
1256 } },
1257 });
1258 },
1259 else => unreachable,
1260 }
1261
1262 break :result MCValue{ .register = dst_reg };
1263 } else {
1264 // const dst_mcv = try self.allocRegOrMem(inst, false);
1265 return self.fail("TODO implement slice_elem_val for elem_size >= 4", .{});
1266 }
1267
1268 _ = offset_mcv;
1269 _ = slice_mcv;
1270 _ = index_mcv;
1271 _ = offset_mcv;
1272 };
11881273 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11891274}
11901275
......@@ -1839,6 +1924,58 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai
18391924 return dst_mcv;
18401925}
18411926
1927fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_index: Liveness.OperandInt, imm: u32) !MCValue {
1928 const mcv = try self.resolveInst(op);
1929 const rhs = MCValue{ .immediate = imm };
1930
1931 const lhs_is_register = mcv == .register;
1932 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op, op_index, mcv);
1933
1934 // Destination must be a register
1935 // LHS must be a register
1936 // RHS must be a register
1937 var dst_mcv: MCValue = undefined;
1938 var lhs_mcv: MCValue = mcv;
1939 var rhs_mcv: MCValue = rhs;
1940
1941 // Allocate registers for operands and/or destination
1942 if (reuse_lhs) {
1943 // Allocate 1 register
1944 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(null, &.{mcv.register}) };
1945 dst_mcv = mcv;
1946 } else {
1947 // Allocate 1 or 2 registers
1948 if (lhs_is_register) {
1949 // Move RHS to register
1950 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{mcv.register}) };
1951 rhs_mcv = dst_mcv;
1952 } else {
1953 // Move LHS and RHS to register
1954 const regs = try self.register_manager.allocRegs(2, .{ inst, null }, &.{});
1955 lhs_mcv = MCValue{ .register = regs[0] };
1956 rhs_mcv = MCValue{ .register = regs[1] };
1957 dst_mcv = lhs_mcv;
1958 }
1959 }
1960
1961 // Move the operands to the newly allocated registers
1962 if (!lhs_is_register) {
1963 try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, mcv);
1964 }
1965 try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs);
1966
1967 _ = try self.addInst(.{
1968 .tag = .mul,
1969 .cond = .al,
1970 .data = .{ .rrr = .{
1971 .rd = dst_mcv.register,
1972 .rn = lhs_mcv.register,
1973 .rm = rhs_mcv.register,
1974 } },
1975 });
1976 return dst_mcv;
1977}
1978
18421979fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
18431980 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
18441981 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
src/codegen.zig+111-63
......@@ -168,9 +168,10 @@ pub fn generateSymbol(
168168 ),
169169 };
170170 },
171 .Array => {
172 // TODO populate .debug_info for the array
173 if (typed_value.val.castTag(.bytes)) |payload| {
171 .Array => switch (typed_value.val.tag()) {
172 .bytes => {
173 // TODO populate .debug_info for the array
174 const payload = typed_value.val.castTag(.bytes).?;
174175 if (typed_value.ty.sentinel()) |sentinel| {
175176 try code.ensureUnusedCapacity(payload.data.len + 1);
176177 code.appendSliceAssumeCapacity(payload.data);
......@@ -188,94 +189,83 @@ pub fn generateSymbol(
188189 } else {
189190 return Result{ .externally_managed = payload.data };
190191 }
191 }
192 return Result{
192 },
193 .array => {
194 // TODO populate .debug_info for the array
195 const elem_vals = typed_value.val.castTag(.array).?.data;
196 const elem_ty = typed_value.ty.elemType();
197 for (elem_vals) |elem_val| {
198 switch (try generateSymbol(bin_file, src_loc, .{
199 .ty = elem_ty,
200 .val = elem_val,
201 }, code, debug_output)) {
202 .appended => {},
203 .externally_managed => |slice| {
204 code.appendSliceAssumeCapacity(slice);
205 return Result{ .appended = {} };
206 },
207 .fail => |em| return Result{ .fail = em },
208 }
209 }
210 return Result{ .appended = {} };
211 },
212 else => return Result{
193213 .fail = try ErrorMsg.create(
194214 bin_file.allocator,
195215 src_loc,
196 "TODO implement generateSymbol for more kinds of arrays",
197 .{},
216 "TODO implement generateSymbol for array type value: {s}",
217 .{@tagName(typed_value.val.tag())},
198218 ),
199 };
219 },
200220 },
201 .Pointer => switch (typed_value.ty.ptrSize()) {
202 .Slice => {
221 .Pointer => switch (typed_value.val.tag()) {
222 .variable => {
223 const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
224 return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
225 },
226 .decl_ref => {
227 const decl = typed_value.val.castTag(.decl_ref).?.data;
228 return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
229 },
230 .slice => {
203231 // TODO populate .debug_info for the slice
232 const slice = typed_value.val.castTag(.slice).?.data;
204233
205234 // generate ptr
206235 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
207236 const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
208237 switch (try generateSymbol(bin_file, src_loc, .{
209238 .ty = slice_ptr_field_type,
210 .val = typed_value.val.slicePtr(),
239 .val = slice.ptr,
211240 }, code, debug_output)) {
212241 .appended => {},
213 .externally_managed => |slice| {
214 code.appendSliceAssumeCapacity(slice);
242 .externally_managed => |external_slice| {
243 code.appendSliceAssumeCapacity(external_slice);
215244 },
216245 .fail => |em| return Result{ .fail = em },
217246 }
218247
219248 // generate length
220 var int_buffer: Value.Payload.U64 = .{
221 .base = .{ .tag = .int_u64 },
222 .data = typed_value.val.sliceLen(),
223 };
224249 switch (try generateSymbol(bin_file, src_loc, .{
225250 .ty = Type.initTag(.usize),
226 .val = Value.initPayload(&int_buffer.base),
251 .val = slice.len,
227252 }, code, debug_output)) {
228253 .appended => {},
229 .externally_managed => |slice| {
230 code.appendSliceAssumeCapacity(slice);
254 .externally_managed => |external_slice| {
255 code.appendSliceAssumeCapacity(external_slice);
231256 },
232257 .fail => |em| return Result{ .fail = em },
233258 }
234259
235 return Result{
236 .fail = try ErrorMsg.create(
237 bin_file.allocator,
238 src_loc,
239 "TODO implement generateSymbol for slice {}",
240 .{typed_value.val},
241 ),
242 };
260 return Result{ .appended = {} };
243261 },
244 else => {
245 // TODO populate .debug_info for the pointer
246 if (typed_value.val.castTag(.decl_ref)) |payload| {
247 const decl = payload.data;
248 if (decl.analysis != .complete) return error.AnalysisFail;
249 decl.alive = true;
250 // TODO handle the dependency of this symbol on the decl's vaddr.
251 // If the decl changes vaddr, then this symbol needs to get regenerated.
252 const vaddr = bin_file.getDeclVAddr(decl);
253 const endian = bin_file.options.target.cpu.arch.endian();
254 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
255 16 => {
256 try code.resize(2);
257 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
258 },
259 32 => {
260 try code.resize(4);
261 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
262 },
263 64 => {
264 try code.resize(8);
265 mem.writeInt(u64, code.items[0..8], vaddr, endian);
266 },
267 else => unreachable,
268 }
269 return Result{ .appended = {} };
270 }
271 return Result{
272 .fail = try ErrorMsg.create(
273 bin_file.allocator,
274 src_loc,
275 "TODO implement generateSymbol for pointer {}",
276 .{typed_value.val},
277 ),
278 };
262 else => return Result{
263 .fail = try ErrorMsg.create(
264 bin_file.allocator,
265 src_loc,
266 "TODO implement generateSymbol for pointer type value: '{s}'",
267 .{@tagName(typed_value.val.tag())},
268 ),
279269 },
280270 },
281271 .Int => {
......@@ -401,3 +391,61 @@ pub fn generateSymbol(
401391 },
402392 }
403393}
394
395fn lowerDeclRef(
396 bin_file: *link.File,
397 src_loc: Module.SrcLoc,
398 typed_value: TypedValue,
399 decl: *Module.Decl,
400 code: *std.ArrayList(u8),
401 debug_output: DebugInfoOutput,
402) GenerateSymbolError!Result {
403 if (typed_value.ty.isSlice()) {
404 // generate ptr
405 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
406 const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
407 switch (try generateSymbol(bin_file, src_loc, .{
408 .ty = slice_ptr_field_type,
409 .val = typed_value.val,
410 }, code, debug_output)) {
411 .appended => {},
412 .externally_managed => |external_slice| {
413 code.appendSliceAssumeCapacity(external_slice);
414 },
415 .fail => |em| return Result{ .fail = em },
416 }
417
418 // generate length
419 var slice_len: Value.Payload.U64 = .{
420 .base = .{ .tag = .int_u64 },
421 .data = typed_value.val.sliceLen(),
422 };
423 switch (try generateSymbol(bin_file, src_loc, .{
424 .ty = Type.initTag(.usize),
425 .val = Value.initPayload(&slice_len.base),
426 }, code, debug_output)) {
427 .appended => {},
428 .externally_managed => |external_slice| {
429 code.appendSliceAssumeCapacity(external_slice);
430 },
431 .fail => |em| return Result{ .fail = em },
432 }
433
434 return Result{ .appended = {} };
435 }
436
437 if (decl.analysis != .complete) return error.AnalysisFail;
438 decl.alive = true;
439 // TODO handle the dependency of this symbol on the decl's vaddr.
440 // If the decl changes vaddr, then this symbol needs to get regenerated.
441 const vaddr = bin_file.getDeclVAddr(decl);
442 const endian = bin_file.options.target.cpu.arch.endian();
443 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
444 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
445 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, vaddr), endian),
446 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
447 else => unreachable,
448 }
449
450 return Result{ .appended = {} };
451}
test/stage2/arm.zig+21
......@@ -637,4 +637,25 @@ pub fn addCases(ctx: *TestContext) !void {
637637 "Hello, World!\n",
638638 );
639639 }
640
641 {
642 var case = ctx.exe("slices", linux_arm);
643 case.addCompareOutput(
644 \\var array = [_]u32{ 0, 42, 123, 69 };
645 \\var s: []const u32 = &array;
646 \\
647 \\pub fn main() void {
648 \\ assert(s[0] == 0);
649 \\ assert(s[1] == 42);
650 \\ assert(s[2] == 123);
651 \\ assert(s[3] == 69);
652 \\}
653 \\
654 \\fn assert(ok: bool) void {
655 \\ if (!ok) unreachable;
656 \\}
657 ,
658 "",
659 );
660 }
640661}