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 {...@@ -130,6 +130,14 @@ fn exit2(code: usize) noreturn {
130 : "memory", "cc"130 : "memory", "cc"
131 );131 );
132 },132 },
133 .riscv64 => {
134 asm volatile ("ecall"
135 :
136 : [number] "{a7}" (94),
137 [arg1] "{a0}" (0),
138 : "rcx", "r11", "memory"
139 );
140 },
133 else => @compileError("TODO"),141 else => @compileError("TODO"),
134 },142 },
135 // exits(0)143 // exits(0)
src/arch/riscv64/CodeGen.zig+202-21
...@@ -363,17 +363,114 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {...@@ -363,17 +363,114 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
363}363}
364364
365fn gen(self: *Self) !void {365fn gen(self: *Self) !void {
366 _ = try self.addInst(.{366 const cc = self.fn_type.fnCallingConvention();
367 .tag = .dbg_prologue_end,367 if (cc != .Naked) {
368 .data = .{ .nop = {} },368 // TODO Finish function prologue and epilogue for riscv64.
369 });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(.{391 // sd s0, 0(sp)
374 .tag = .dbg_epilogue_begin,392 _ = try self.addInst(.{
375 .data = .{ .nop = {} },393 .tag = .sd,
376 });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
378 // Drop them off at the rbrace.475 // Drop them off at the rbrace.
379 _ = try self.addInst(.{476 _ = try self.addInst(.{
...@@ -1317,7 +1414,36 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1317,7 +1414,36 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1317 // Due to incremental compilation, how function calls are generated depends1414 // Due to incremental compilation, how function calls are generated depends
1318 // on linking.1415 // on linking.
1319 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {1416 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
1322 if (self.air.value(callee)) |func_value| {1448 if (self.air.value(callee)) |func_value| {
1323 if (func_value.castTag(.function)) |func_payload| {1449 if (func_value.castTag(.function)) |func_payload| {
...@@ -1386,14 +1512,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1386,14 +1512,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1386fn ret(self: *Self, mcv: MCValue) !void {1512fn ret(self: *Self, mcv: MCValue) !void {
1387 const ret_ty = self.fn_type.fnReturnType();1513 const ret_ty = self.fn_type.fnReturnType();
1388 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);1514 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1389 _ = try self.addInst(.{1515 // Just add space for an instruction, patch this later
1390 .tag = .jalr,1516 const index = try self.addInst(.{
1391 .data = .{ .i_type = .{1517 .tag = .nop,
1392 .rd = .zero,1518 .data = .{ .nop = {} },
1393 .rs1 = .ra,
1394 .imm12 = 0,
1395 } },
1396 });1519 });
1520 try self.exitlude_jump_relocs.append(self.gpa, index);
1397}1521}
13981522
1399fn airRet(self: *Self, inst: Air.Inst.Index) !void {1523fn airRet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2156,7 +2280,6 @@ const CallMCValues = struct {...@@ -2156,7 +2280,6 @@ const CallMCValues = struct {
2156/// Caller must call `CallMCValues.deinit`.2280/// Caller must call `CallMCValues.deinit`.
2157fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {2281fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
2158 const cc = fn_ty.fnCallingConvention();2282 const cc = fn_ty.fnCallingConvention();
2159 _ = cc;
2160 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());2283 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
2161 defer self.gpa.free(param_types);2284 defer self.gpa.free(param_types);
2162 fn_ty.fnParamTypes(param_types);2285 fn_ty.fnParamTypes(param_types);
...@@ -2171,15 +2294,73 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -2171,15 +2294,73 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
21712294
2172 const ret_ty = fn_ty.fnReturnType();2295 const ret_ty = fn_ty.fnReturnType();
21732296
2174 if (param_types.len != 0) {2297 switch (cc) {
2175 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch});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}),
2176 }2344 }
21772345
2178 if (ret_ty.zigTypeTag() == .NoReturn) {2346 if (ret_ty.zigTypeTag() == .NoReturn) {
2179 result.return_value = .{ .unreach = {} };2347 result.return_value = .{ .unreach = {} };
2180 } else if (!ret_ty.hasCodeGenBits()) {2348 } else if (!ret_ty.hasCodeGenBits()) {
2181 result.return_value = .{ .none = {} };2349 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 }
2183 return result;2364 return result;
2184}2365}
21852366
src/arch/riscv64/Emit.zig+15
...@@ -46,6 +46,7 @@ pub fn emitMir(...@@ -46,6 +46,7 @@ pub fn emitMir(
46 .addi => try emit.mirIType(inst),46 .addi => try emit.mirIType(inst),
47 .jalr => try emit.mirIType(inst),47 .jalr => try emit.mirIType(inst),
48 .ld => try emit.mirIType(inst),48 .ld => try emit.mirIType(inst),
49 .sd => try emit.mirIType(inst),
4950
50 .ebreak => try emit.mirSystem(inst),51 .ebreak => try emit.mirSystem(inst),
51 .ecall => try emit.mirSystem(inst),52 .ecall => try emit.mirSystem(inst),
...@@ -55,6 +56,9 @@ pub fn emitMir(...@@ -55,6 +56,9 @@ pub fn emitMir(
55 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),56 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
56 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),57 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
5758
59 .nop => try emit.mirNop(inst),
60 .ret => try emit.mirNop(inst),
61
58 .lui => try emit.mirUType(inst),62 .lui => try emit.mirUType(inst),
59 }63 }
60 }64 }
...@@ -135,6 +139,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -135,6 +139,7 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
135 .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)),139 .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)),
136 .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)),140 .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)),
137 .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)),141 .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)),
138 else => unreachable,143 else => unreachable,
139 }144 }
140}145}
...@@ -190,3 +195,13 @@ fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -190,3 +195,13 @@ fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
190 else => unreachable,195 else => unreachable,
191 }196 }
192}197}
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 {...@@ -36,6 +36,9 @@ pub const Inst = struct {
36 jalr,36 jalr,
37 ld,37 ld,
38 lui,38 lui,
39 nop,
40 ret,
41 sd,
39 };42 };
4043
41 /// The position of an MIR instruction within the `Mir` instructions array.44 /// 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 {...@@ -11,10 +11,8 @@ pub fn addCases(ctx: *TestContext) !void {
11 var case = ctx.exe("riscv64 hello world", linux_riscv64);11 var case = ctx.exe("riscv64 hello world", linux_riscv64);
12 // Regular old hello world12 // Regular old hello world
13 case.addCompareOutput(13 case.addCompareOutput(
14 \\pub export fn _start() noreturn {14 \\pub fn main() void {
15 \\ print();15 \\ print();
16 \\
17 \\ exit();
18 \\}16 \\}
19 \\17 \\
20 \\fn print() void {18 \\fn print() void {
...@@ -28,16 +26,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -28,16 +26,6 @@ pub fn addCases(ctx: *TestContext) !void {
28 \\ );26 \\ );
29 \\ return;27 \\ return;
30 \\}28 \\}
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 \\}
41 ,29 ,
42 "Hello, World!\n",30 "Hello, World!\n",
43 );31 );