| 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,13 +1159,29 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1159 | 1159 | ||
| 1160 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { | 1160 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1161 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1171 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1164 | } | 1172 | } |
| 1165 | 1173 | ||
| 1166 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | 1174 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1167 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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,7 +1200,76 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1184 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | 1200 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1185 | const is_volatile = false; // TODO | 1201 | const is_volatile = false; // TODO |
| 1186 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,6 +1924,58 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai |
| 1839 | return dst_mcv; | 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 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { | 1979 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1843 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; | 1980 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; |
| 1844 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; | 1981 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; |
src/codegen.zig+111-63| ... | @@ -168,9 +168,10 @@ pub fn generateSymbol( | ... | @@ -168,9 +168,10 @@ pub fn generateSymbol( |
| 168 | ), | 168 | ), |
| 169 | }; | 169 | }; |
| 170 | }, | 170 | }, |
| 171 | .Array => { | 171 | .Array => switch (typed_value.val.tag()) { |
| 172 | // TODO populate .debug_info for the array | 172 | .bytes => { |
| 173 | if (typed_value.val.castTag(.bytes)) |payload| { | 173 | // TODO populate .debug_info for the array |
| 174 | const payload = typed_value.val.castTag(.bytes).?; | ||
| 174 | if (typed_value.ty.sentinel()) |sentinel| { | 175 | if (typed_value.ty.sentinel()) |sentinel| { |
| 175 | try code.ensureUnusedCapacity(payload.data.len + 1); | 176 | try code.ensureUnusedCapacity(payload.data.len + 1); |
| 176 | code.appendSliceAssumeCapacity(payload.data); | 177 | code.appendSliceAssumeCapacity(payload.data); |
| ... | @@ -188,94 +189,83 @@ pub fn generateSymbol( | ... | @@ -188,94 +189,83 @@ pub fn generateSymbol( |
| 188 | } else { | 189 | } else { |
| 189 | return Result{ .externally_managed = payload.data }; | 190 | return Result{ .externally_managed = payload.data }; |
| 190 | } | 191 | } |
| 191 | } | 192 | }, |
| 192 | return Result{ | 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 | .fail = try ErrorMsg.create( | 213 | .fail = try ErrorMsg.create( |
| 194 | bin_file.allocator, | 214 | bin_file.allocator, |
| 195 | src_loc, | 215 | src_loc, |
| 196 | "TODO implement generateSymbol for more kinds of arrays", | 216 | "TODO implement generateSymbol for array type value: {s}", |
| 197 | .{}, | 217 | .{@tagName(typed_value.val.tag())}, |
| 198 | ), | 218 | ), |
| 199 | }; | 219 | }, |
| 200 | }, | 220 | }, |
| 201 | .Pointer => switch (typed_value.ty.ptrSize()) { | 221 | .Pointer => switch (typed_value.val.tag()) { |
| 202 | .Slice => { | 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 | // TODO populate .debug_info for the slice | 231 | // TODO populate .debug_info for the slice |
| 232 | const slice = typed_value.val.castTag(.slice).?.data; | ||
| 204 | 233 | ||
| 205 | // generate ptr | 234 | // generate ptr |
| 206 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 235 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 207 | const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); | 236 | const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); |
| 208 | switch (try generateSymbol(bin_file, src_loc, .{ | 237 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 209 | .ty = slice_ptr_field_type, | 238 | .ty = slice_ptr_field_type, |
| 210 | .val = typed_value.val.slicePtr(), | 239 | .val = slice.ptr, |
| 211 | }, code, debug_output)) { | 240 | }, code, debug_output)) { |
| 212 | .appended => {}, | 241 | .appended => {}, |
| 213 | .externally_managed => |slice| { | 242 | .externally_managed => |external_slice| { |
| 214 | code.appendSliceAssumeCapacity(slice); | 243 | code.appendSliceAssumeCapacity(external_slice); |
| 215 | }, | 244 | }, |
| 216 | .fail => |em| return Result{ .fail = em }, | 245 | .fail => |em| return Result{ .fail = em }, |
| 217 | } | 246 | } |
| 218 | 247 | ||
| 219 | // generate length | 248 | // generate length |
| 220 | var int_buffer: Value.Payload.U64 = .{ | ||
| 221 | .base = .{ .tag = .int_u64 }, | ||
| 222 | .data = typed_value.val.sliceLen(), | ||
| 223 | }; | ||
| 224 | switch (try generateSymbol(bin_file, src_loc, .{ | 249 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 225 | .ty = Type.initTag(.usize), | 250 | .ty = Type.initTag(.usize), |
| 226 | .val = Value.initPayload(&int_buffer.base), | 251 | .val = slice.len, |
| 227 | }, code, debug_output)) { | 252 | }, code, debug_output)) { |
| 228 | .appended => {}, | 253 | .appended => {}, |
| 229 | .externally_managed => |slice| { | 254 | .externally_managed => |external_slice| { |
| 230 | code.appendSliceAssumeCapacity(slice); | 255 | code.appendSliceAssumeCapacity(external_slice); |
| 231 | }, | 256 | }, |
| 232 | .fail => |em| return Result{ .fail = em }, | 257 | .fail => |em| return Result{ .fail = em }, |
| 233 | } | 258 | } |
| 234 | 259 | ||
| 235 | return Result{ | 260 | return Result{ .appended = {} }; |
| 236 | .fail = try ErrorMsg.create( | ||
| 237 | bin_file.allocator, | ||
| 238 | src_loc, | ||
| 239 | "TODO implement generateSymbol for slice {}", | ||
| 240 | .{typed_value.val}, | ||
| 241 | ), | ||
| 242 | }; | ||
| 243 | }, | 261 | }, |
| 244 | else => { | 262 | else => return Result{ |
| 245 | // TODO populate .debug_info for the pointer | 263 | .fail = try ErrorMsg.create( |
| 246 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 264 | bin_file.allocator, |
| 247 | const decl = payload.data; | 265 | src_loc, |
| 248 | if (decl.analysis != .complete) return error.AnalysisFail; | 266 | "TODO implement generateSymbol for pointer type value: '{s}'", |
| 249 | decl.alive = true; | 267 | .{@tagName(typed_value.val.tag())}, |
| 250 | // TODO handle the dependency of this symbol on the decl's vaddr. | 268 | ), |
| 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 | }; | ||
| 279 | }, | 269 | }, |
| 280 | }, | 270 | }, |
| 281 | .Int => { | 271 | .Int => { |
| ... | @@ -401,3 +391,61 @@ pub fn generateSymbol( | ... | @@ -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,4 +637,25 @@ pub fn addCases(ctx: *TestContext) !void { |
| 637 | "Hello, World!\n", | 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 | } |