authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-20 15:30:53+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-11-20 19:25:59+01:00
loge8112f774450be081cc210cf03c114f64b78731e
treea7dfe45c668d4539fb221f321ef90f334e091047
parentc84147f90dd99af1352e69f698be48964573ee07

stage2 RISCV64: implement basic function prologue and epilogue


5 files changed, 229 insertions(+), 34 deletions(-)

lib/std/start.zig+8
......@@ -130,6 +130,14 @@ fn exit2(code: usize) noreturn {
130130 : "memory", "cc"
131131 );
132132 },
133 .riscv64 => {
134 asm volatile ("ecall"
135 :
136 : [number] "{a7}" (94),
137 [arg1] "{a0}" (0),
138 : "rcx", "r11", "memory"
139 );
140 },
133141 else => @compileError("TODO"),
134142 },
135143 // exits(0)
src/arch/riscv64/CodeGen.zig+202-21
......@@ -363,17 +363,114 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
363363}
364364
365365fn gen(self: *Self) !void {
366 _ = try self.addInst(.{
367 .tag = .dbg_prologue_end,
368 .data = .{ .nop = {} },
369 });
366 const cc = self.fn_type.fnCallingConvention();
367 if (cc != .Naked) {
368 // TODO Finish function prologue and epilogue for riscv64.
369
370 // TODO Backpatch stack offset
371 // addi sp, sp, -16
372 _ = try self.addInst(.{
373 .tag = .addi,
374 .data = .{ .i_type = .{
375 .rd = .sp,
376 .rs1 = .sp,
377 .imm12 = -16,
378 } },
379 });
370380
371 try self.genBody(self.air.getMainBody());
381 // sd ra, 8(sp)
382 _ = try self.addInst(.{
383 .tag = .sd,
384 .data = .{ .i_type = .{
385 .rd = .ra,
386 .rs1 = .sp,
387 .imm12 = 8,
388 } },
389 });
372390
373 _ = try self.addInst(.{
374 .tag = .dbg_epilogue_begin,
375 .data = .{ .nop = {} },
376 });
391 // sd s0, 0(sp)
392 _ = try self.addInst(.{
393 .tag = .sd,
394 .data = .{ .i_type = .{
395 .rd = .s0,
396 .rs1 = .sp,
397 .imm12 = 0,
398 } },
399 });
400
401 _ = try self.addInst(.{
402 .tag = .dbg_prologue_end,
403 .data = .{ .nop = {} },
404 });
405
406 try self.genBody(self.air.getMainBody());
407
408 _ = try self.addInst(.{
409 .tag = .dbg_epilogue_begin,
410 .data = .{ .nop = {} },
411 });
412
413 // exitlude jumps
414 if (self.exitlude_jump_relocs.items.len == 1) {
415 // There is only one relocation. Hence,
416 // this relocation must be at the end of
417 // the code. Therefore, we can just delete
418 // the space initially reserved for the
419 // jump
420 self.mir_instructions.len -= 1;
421 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
422 _ = jmp_reloc;
423 return self.fail("TODO add branches in RISCV64", .{});
424 }
425
426 // ld ra, 8(sp)
427 _ = try self.addInst(.{
428 .tag = .ld,
429 .data = .{ .i_type = .{
430 .rd = .ra,
431 .rs1 = .sp,
432 .imm12 = 8,
433 } },
434 });
435
436 // ld s0, 0(sp)
437 _ = try self.addInst(.{
438 .tag = .ld,
439 .data = .{ .i_type = .{
440 .rd = .s0,
441 .rs1 = .sp,
442 .imm12 = 0,
443 } },
444 });
445
446 // addi sp, sp, 16
447 _ = try self.addInst(.{
448 .tag = .addi,
449 .data = .{ .i_type = .{
450 .rd = .sp,
451 .rs1 = .sp,
452 .imm12 = 16,
453 } },
454 });
455
456 // ret
457 _ = try self.addInst(.{
458 .tag = .ret,
459 .data = .{ .nop = {} },
460 });
461 } else {
462 _ = try self.addInst(.{
463 .tag = .dbg_prologue_end,
464 .data = .{ .nop = {} },
465 });
466
467 try self.genBody(self.air.getMainBody());
468
469 _ = try self.addInst(.{
470 .tag = .dbg_epilogue_begin,
471 .data = .{ .nop = {} },
472 });
473 }
377474
378475 // Drop them off at the rbrace.
379476 _ = try self.addInst(.{
......@@ -1317,7 +1414,36 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
13171414 // Due to incremental compilation, how function calls are generated depends
13181415 // on linking.
13191416 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1320 if (info.args.len > 0) return self.fail("TODO implement fn args for {}", .{self.target.cpu.arch});
1417 for (info.args) |mc_arg, arg_i| {
1418 const arg = args[arg_i];
1419 const arg_ty = self.air.typeOf(arg);
1420 const arg_mcv = try self.resolveInst(args[arg_i]);
1421
1422 switch (mc_arg) {
1423 .none => continue,
1424 .undef => unreachable,
1425 .immediate => unreachable,
1426 .unreach => unreachable,
1427 .dead => unreachable,
1428 .embedded_in_code => unreachable,
1429 .memory => unreachable,
1430 .compare_flags_signed => unreachable,
1431 .compare_flags_unsigned => unreachable,
1432 .register => |reg| {
1433 try self.register_manager.getReg(reg, null);
1434 try self.genSetReg(arg_ty, reg, arg_mcv);
1435 },
1436 .stack_offset => {
1437 return self.fail("TODO implement calling with parameters in memory", .{});
1438 },
1439 .ptr_stack_offset => {
1440 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1441 },
1442 .ptr_embedded_in_code => {
1443 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1444 },
1445 }
1446 }
13211447
13221448 if (self.air.value(callee)) |func_value| {
13231449 if (func_value.castTag(.function)) |func_payload| {
......@@ -1386,14 +1512,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
13861512fn ret(self: *Self, mcv: MCValue) !void {
13871513 const ret_ty = self.fn_type.fnReturnType();
13881514 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1389 _ = try self.addInst(.{
1390 .tag = .jalr,
1391 .data = .{ .i_type = .{
1392 .rd = .zero,
1393 .rs1 = .ra,
1394 .imm12 = 0,
1395 } },
1515 // Just add space for an instruction, patch this later
1516 const index = try self.addInst(.{
1517 .tag = .nop,
1518 .data = .{ .nop = {} },
13961519 });
1520 try self.exitlude_jump_relocs.append(self.gpa, index);
13971521}
13981522
13991523fn airRet(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2156,7 +2280,6 @@ const CallMCValues = struct {
21562280/// Caller must call `CallMCValues.deinit`.
21572281fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
21582282 const cc = fn_ty.fnCallingConvention();
2159 _ = cc;
21602283 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
21612284 defer self.gpa.free(param_types);
21622285 fn_ty.fnParamTypes(param_types);
......@@ -2171,15 +2294,73 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
21712294
21722295 const ret_ty = fn_ty.fnReturnType();
21732296
2174 if (param_types.len != 0) {
2175 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch});
2297 switch (cc) {
2298 .Naked => {
2299 assert(result.args.len == 0);
2300 result.return_value = .{ .unreach = {} };
2301 result.stack_byte_count = 0;
2302 result.stack_align = 1;
2303 return result;
2304 },
2305 .Unspecified, .C => {
2306 // LP64D ABI
2307 //
2308 // TODO make this generic with other ABIs, in particular
2309 // with different hardware floating-point calling
2310 // conventions
2311 var next_register: usize = 0;
2312 var next_stack_offset: u32 = 0;
2313 const argument_registers = [_]Register{ .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7 };
2314
2315 for (param_types) |ty, i| {
2316 const param_size = @intCast(u32, ty.abiSize(self.target.*));
2317 if (param_size <= 8) {
2318 if (next_register < argument_registers.len) {
2319 result.args[i] = .{ .register = argument_registers[next_register] };
2320 next_register += 1;
2321 } else {
2322 result.args[i] = .{ .stack_offset = next_stack_offset };
2323 next_register += next_stack_offset;
2324 }
2325 } else if (param_size <= 16) {
2326 if (next_register < argument_registers.len - 1) {
2327 return self.fail("TODO MCValues with 2 registers", .{});
2328 } else if (next_register < argument_registers.len) {
2329 return self.fail("TODO MCValues split register + stack", .{});
2330 } else {
2331 result.args[i] = .{ .stack_offset = next_stack_offset };
2332 next_register += next_stack_offset;
2333 }
2334 } else {
2335 result.args[i] = .{ .stack_offset = next_stack_offset };
2336 next_register += next_stack_offset;
2337 }
2338 }
2339
2340 result.stack_byte_count = next_stack_offset;
2341 result.stack_align = 16;
2342 },
2343 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
21762344 }
21772345
21782346 if (ret_ty.zigTypeTag() == .NoReturn) {
21792347 result.return_value = .{ .unreach = {} };
21802348 } else if (!ret_ty.hasCodeGenBits()) {
21812349 result.return_value = .{ .none = {} };
2182 } else return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch});
2350 } else switch (cc) {
2351 .Naked => unreachable,
2352 .Unspecified, .C => {
2353 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2354 if (ret_ty_size <= 8) {
2355 result.return_value = .{ .register = .a0 };
2356 } else if (ret_ty_size <= 16) {
2357 return self.fail("TODO support MCValue 2 registers", .{});
2358 } else {
2359 return self.fail("TODO support return by reference", .{});
2360 }
2361 },
2362 else => return self.fail("TODO implement function return values for {}", .{cc}),
2363 }
21832364 return result;
21842365}
21852366
src/arch/riscv64/Emit.zig+15
......@@ -46,6 +46,7 @@ pub fn emitMir(
4646 .addi => try emit.mirIType(inst),
4747 .jalr => try emit.mirIType(inst),
4848 .ld => try emit.mirIType(inst),
49 .sd => try emit.mirIType(inst),
4950
5051 .ebreak => try emit.mirSystem(inst),
5152 .ecall => try emit.mirSystem(inst),
......@@ -55,6 +56,9 @@ pub fn emitMir(
5556 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
5657 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
5758
59 .nop => try emit.mirNop(inst),
60 .ret => try emit.mirNop(inst),
61
5862 .lui => try emit.mirUType(inst),
5963 }
6064 }
......@@ -135,6 +139,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
135139 .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)),
136140 .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)),
137141 .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)),
142 .sd => try emit.writeInstruction(Instruction.sd(i_type.rd, i_type.imm12, i_type.rs1)),
138143 else => unreachable,
139144 }
140145}
......@@ -190,3 +195,13 @@ fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
190195 else => unreachable,
191196 }
192197}
198
199fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void {
200 const tag = emit.mir.instructions.items(.tag)[inst];
201
202 switch (tag) {
203 .nop => try emit.writeInstruction(Instruction.addi(.zero, .zero, 0)),
204 .ret => try emit.writeInstruction(Instruction.jalr(.zero, 0, .ra)),
205 else => unreachable,
206 }
207}
src/arch/riscv64/Mir.zig+3
......@@ -36,6 +36,9 @@ pub const Inst = struct {
3636 jalr,
3737 ld,
3838 lui,
39 nop,
40 ret,
41 sd,
3942 };
4043
4144 /// The position of an MIR instruction within the `Mir` instructions array.
test/stage2/riscv64.zig+1-13
......@@ -11,10 +11,8 @@ pub fn addCases(ctx: *TestContext) !void {
1111 var case = ctx.exe("riscv64 hello world", linux_riscv64);
1212 // Regular old hello world
1313 case.addCompareOutput(
14 \\pub export fn _start() noreturn {
14 \\pub fn main() void {
1515 \\ print();
16 \\
17 \\ exit();
1816 \\}
1917 \\
2018 \\fn print() void {
......@@ -28,16 +26,6 @@ pub fn addCases(ctx: *TestContext) !void {
2826 \\ );
2927 \\ return;
3028 \\}
31 \\
32 \\fn exit() noreturn {
33 \\ asm volatile ("ecall"
34 \\ :
35 \\ : [number] "{a7}" (94),
36 \\ [arg1] "{a0}" (0)
37 \\ : "rcx", "r11", "memory"
38 \\ );
39 \\ unreachable;
40 \\}
4129 ,
4230 "Hello, World!\n",
4331 );