| author | |
| committer | |
| log | c0ae9647f9656ea47c49ffd64443b7da73aeffc7 |
| tree | 3f39f2283482c3dd05b44404d967475fb495df4c |
| parent | 4f4f0bc6f03a84597247504a5ee15ed7f1e050d1 |
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 { |
| 1159 | 1159 | |
| 1160 | 1160 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1161 | 1161 | 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 | }; | |
| 1163 | 1171 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1164 | 1172 | } |
| 1165 | 1173 | |
| 1166 | 1174 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1167 | 1175 | 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 | }; | |
| 1169 | 1185 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1170 | 1186 | } |
| 1171 | 1187 | |
| ... | ... | @@ -1184,7 +1200,76 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1184 | 1200 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1185 | 1201 | const is_volatile = false; // TODO |
| 1186 | 1202 | 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 | }; | |
| 1188 | 1273 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1189 | 1274 | } |
| 1190 | 1275 | |
| ... | ... | @@ -1839,6 +1924,58 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai |
| 1839 | 1924 | return dst_mcv; |
| 1840 | 1925 | } |
| 1841 | 1926 | |
| 1927 | fn 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 | ||
| 1842 | 1979 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1843 | 1980 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; |
| 1844 | 1981 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; |
src/codegen.zig+111-63| ... | ... | @@ -168,9 +168,10 @@ pub fn generateSymbol( |
| 168 | 168 | ), |
| 169 | 169 | }; |
| 170 | 170 | }, |
| 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).?; | |
| 174 | 175 | if (typed_value.ty.sentinel()) |sentinel| { |
| 175 | 176 | try code.ensureUnusedCapacity(payload.data.len + 1); |
| 176 | 177 | code.appendSliceAssumeCapacity(payload.data); |
| ... | ... | @@ -188,94 +189,83 @@ pub fn generateSymbol( |
| 188 | 189 | } else { |
| 189 | 190 | return Result{ .externally_managed = payload.data }; |
| 190 | 191 | } |
| 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{ | |
| 193 | 213 | .fail = try ErrorMsg.create( |
| 194 | 214 | bin_file.allocator, |
| 195 | 215 | 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())}, | |
| 198 | 218 | ), |
| 199 | }; | |
| 219 | }, | |
| 200 | 220 | }, |
| 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 => { | |
| 203 | 231 | // TODO populate .debug_info for the slice |
| 232 | const slice = typed_value.val.castTag(.slice).?.data; | |
| 204 | 233 | |
| 205 | 234 | // generate ptr |
| 206 | 235 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 207 | 236 | const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); |
| 208 | 237 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 209 | 238 | .ty = slice_ptr_field_type, |
| 210 | .val = typed_value.val.slicePtr(), | |
| 239 | .val = slice.ptr, | |
| 211 | 240 | }, code, debug_output)) { |
| 212 | 241 | .appended => {}, |
| 213 | .externally_managed => |slice| { | |
| 214 | code.appendSliceAssumeCapacity(slice); | |
| 242 | .externally_managed => |external_slice| { | |
| 243 | code.appendSliceAssumeCapacity(external_slice); | |
| 215 | 244 | }, |
| 216 | 245 | .fail => |em| return Result{ .fail = em }, |
| 217 | 246 | } |
| 218 | 247 | |
| 219 | 248 | // generate length |
| 220 | var int_buffer: Value.Payload.U64 = .{ | |
| 221 | .base = .{ .tag = .int_u64 }, | |
| 222 | .data = typed_value.val.sliceLen(), | |
| 223 | }; | |
| 224 | 249 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 225 | 250 | .ty = Type.initTag(.usize), |
| 226 | .val = Value.initPayload(&int_buffer.base), | |
| 251 | .val = slice.len, | |
| 227 | 252 | }, code, debug_output)) { |
| 228 | 253 | .appended => {}, |
| 229 | .externally_managed => |slice| { | |
| 230 | code.appendSliceAssumeCapacity(slice); | |
| 254 | .externally_managed => |external_slice| { | |
| 255 | code.appendSliceAssumeCapacity(external_slice); | |
| 231 | 256 | }, |
| 232 | 257 | .fail => |em| return Result{ .fail = em }, |
| 233 | 258 | } |
| 234 | 259 | |
| 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 = {} }; | |
| 243 | 261 | }, |
| 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 | ), | |
| 279 | 269 | }, |
| 280 | 270 | }, |
| 281 | 271 | .Int => { |
| ... | ... | @@ -401,3 +391,61 @@ pub fn generateSymbol( |
| 401 | 391 | }, |
| 402 | 392 | } |
| 403 | 393 | } |
| 394 | ||
| 395 | fn 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 { |
| 637 | 637 | "Hello, World!\n", |
| 638 | 638 | ); |
| 639 | 639 | } |
| 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 | } | |
| 640 | 661 | } |