authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-29 17:56:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-29 17:56:30-07:00
logd18b6785bb394955eb092c82818e0214e456aced
tree8ff1a615bc30e0bbc707a2934cca8a969ec4a314
parentbbe2cca1ae32af322abcf4cc4a6d6bee671bf5b8

stage2: C backend improvements

* Module: improve doc comments * C backend: improve const-correctness * C backend: introduce renderTypeAndName * C backend: put `static` on functions when appropriate * C backend: fix not handling errors in genBinOp * C backend: handle more IR instructions - alloc, store, boolean comparisons, ret_ptr * C backend: call instruction properly stores its result * test harness: ensure execution tests have empty stderr

6 files changed, 178 insertions(+), 81 deletions(-)

src/Module.zig+1
...@@ -37,6 +37,7 @@ root_scope: *Scope,...@@ -37,6 +37,7 @@ root_scope: *Scope,
37/// It's rare for a decl to be exported, so we save memory by having a sparse map of37/// It's rare for a decl to be exported, so we save memory by having a sparse map of
38/// Decl pointers to details about them being exported.38/// Decl pointers to details about them being exported.
39/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.39/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
40/// The slice is guaranteed to not be empty.
40decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},41decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
41/// We track which export is associated with the given symbol name for quick42/// We track which export is associated with the given symbol name for quick
42/// detection of symbol collisions.43/// detection of symbol collisions.
src/codegen/c.zig+129-56
...@@ -21,6 +21,34 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {...@@ -21,6 +21,34 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
21 return allocator.dupe(u8, name);21 return allocator.dupe(u8, name);
22}22}
2323
24const Mutability = enum { Const, Mut };
25
26fn renderTypeAndName(
27 ctx: *Context,
28 writer: Writer,
29 ty: Type,
30 name: []const u8,
31 mutability: Mutability,
32) error{ OutOfMemory, AnalysisFail }!void {
33 var suffix = std.ArrayList(u8).init(&ctx.arena.allocator);
34
35 var render_ty = ty;
36 while (render_ty.zigTypeTag() == .Array) {
37 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
38 const c_len = render_ty.arrayLen() + sentinel_bit;
39 try suffix.writer().print("[{d}]", .{c_len});
40 render_ty = render_ty.elemType();
41 }
42
43 try renderType(ctx, writer, render_ty);
44
45 const const_prefix = switch (mutability) {
46 .Const => "const ",
47 .Mut => "",
48 };
49 try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items });
50}
51
24fn renderType(52fn renderType(
25 ctx: *Context,53 ctx: *Context,
26 writer: Writer,54 writer: Writer,
...@@ -74,14 +102,14 @@ fn renderType(...@@ -74,14 +102,14 @@ fn renderType(
74 if (t.isSlice()) {102 if (t.isSlice()) {
75 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});103 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});
76 } else {104 } else {
105 try renderType(ctx, writer, t.elemType());
106 try writer.writeAll(" *");
77 if (t.isConstPtr()) {107 if (t.isConstPtr()) {
78 try writer.writeAll("const ");108 try writer.writeAll("const ");
79 }109 }
80 if (t.isVolatilePtr()) {110 if (t.isVolatilePtr()) {
81 try writer.writeAll("volatile ");111 try writer.writeAll("volatile ");
82 }112 }
83 try renderType(ctx, writer, t.elemType());
84 try writer.writeAll(" *");
85 }113 }
86 },114 },
87 .Array => {115 .Array => {
...@@ -176,12 +204,27 @@ fn renderFunctionSignature(...@@ -176,12 +204,27 @@ fn renderFunctionSignature(
176 decl: *Decl,204 decl: *Decl,
177) !void {205) !void {
178 const tv = decl.typed_value.most_recent.typed_value;206 const tv = decl.typed_value.most_recent.typed_value;
207 // Determine whether the function is globally visible.
208 const is_global = blk: {
209 switch (tv.val.tag()) {
210 .extern_fn => break :blk true,
211 .function => {
212 const func = tv.val.cast(Value.Payload.Function).?.func;
213 break :blk ctx.module.decl_exports.contains(func.owner_decl);
214 },
215 else => unreachable,
216 }
217 };
218 if (!is_global) {
219 try writer.writeAll("static ");
220 }
179 try renderType(ctx, writer, tv.ty.fnReturnType());221 try renderType(ctx, writer, tv.ty.fnReturnType());
180 // Use the child allocator directly, as we know the name can be freed before222 // Use the child allocator directly, as we know the name can be freed before
181 // the rest of the arena.223 // the rest of the arena.
182 const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name));224 const decl_name = mem.span(decl.name);
225 const name = try map(ctx.arena.child_allocator, decl_name);
183 defer ctx.arena.child_allocator.free(name);226 defer ctx.arena.child_allocator.free(name);
184 try writer.print(" {}(", .{name});227 try writer.print(" {s}(", .{name});
185 var param_len = tv.ty.fnParamLen();228 var param_len = tv.ty.fnParamLen();
186 if (param_len == 0)229 if (param_len == 0)
187 try writer.writeAll("void")230 try writer.writeAll("void")
...@@ -205,7 +248,7 @@ fn indent(file: *C) !void {...@@ -205,7 +248,7 @@ fn indent(file: *C) !void {
205 try file.main.writer().writeByteNTimes(' ', indent_amt);248 try file.main.writer().writeByteNTimes(' ', indent_amt);
206}249}
207250
208pub fn generate(file: *C, decl: *Decl) !void {251pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
209 const tv = decl.typed_value.most_recent.typed_value;252 const tv = decl.typed_value.most_recent.typed_value;
210253
211 var arena = std.heap.ArenaAllocator.init(file.base.allocator);254 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
...@@ -218,6 +261,7 @@ pub fn generate(file: *C, decl: *Decl) !void {...@@ -218,6 +261,7 @@ pub fn generate(file: *C, decl: *Decl) !void {
218 .inst_map = &inst_map,261 .inst_map = &inst_map,
219 .target = file.base.options.target,262 .target = file.base.options.target,
220 .header = &file.header,263 .header = &file.header,
264 .module = module,
221 };265 };
222 defer {266 defer {
223 file.error_msg = ctx.error_msg;267 file.error_msg = ctx.error_msg;
...@@ -236,17 +280,26 @@ pub fn generate(file: *C, decl: *Decl) !void {...@@ -236,17 +280,26 @@ pub fn generate(file: *C, decl: *Decl) !void {
236 try writer.writeAll("\n");280 try writer.writeAll("\n");
237 for (instructions) |inst| {281 for (instructions) |inst| {
238 if (switch (inst.tag) {282 if (switch (inst.tag) {
283 .add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"),
284 .alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?),
285 .arg => try genArg(&ctx),
239 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),286 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
287 .block => try genBlock(&ctx, file, inst.castTag(.block).?),
288 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
240 .call => try genCall(&ctx, file, inst.castTag(.call).?),289 .call => try genCall(&ctx, file, inst.castTag(.call).?),
241 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),290 .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
242 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),291 .cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"),
292 .cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="),
293 .cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"),
294 .cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="),
295 .cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
296 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
297 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
243 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),298 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),
244 .retvoid => try genRetVoid(file),299 .retvoid => try genRetVoid(file),
245 .arg => try genArg(&ctx),300 .store => try genStore(&ctx, file, inst.castTag(.store).?),
246 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),301 .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"),
247 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
248 .unreach => try genUnreach(file, inst.castTag(.unreach).?),302 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
249 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
250 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),303 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
251 }) |name| {304 }) |name| {
252 try ctx.inst_map.putNoClobber(inst, name);305 try ctx.inst_map.putNoClobber(inst, name);
...@@ -264,19 +317,7 @@ pub fn generate(file: *C, decl: *Decl) !void {...@@ -264,19 +317,7 @@ pub fn generate(file: *C, decl: *Decl) !void {
264 // TODO ask the Decl if it is const317 // TODO ask the Decl if it is const
265 // https://github.com/ziglang/zig/issues/7582318 // https://github.com/ziglang/zig/issues/7582
266319
267 var suffix = std.ArrayList(u8).init(file.base.allocator);320 try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut);
268 defer suffix.deinit();
269
270 var render_ty = tv.ty;
271 while (render_ty.zigTypeTag() == .Array) {
272 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
273 const c_len = render_ty.arrayLen() + sentinel_bit;
274 try suffix.writer().print("[{d}]", .{c_len});
275 render_ty = render_ty.elemType();
276 }
277
278 try renderType(&ctx, writer, render_ty);
279 try writer.print(" {s}{s}", .{ decl.name, suffix.items });
280321
281 try writer.writeAll(" = ");322 try writer.writeAll(" = ");
282 try renderValue(&ctx, writer, tv.ty, tv.val);323 try renderValue(&ctx, writer, tv.ty, tv.val);
...@@ -304,6 +345,7 @@ pub fn generateHeader(...@@ -304,6 +345,7 @@ pub fn generateHeader(
304 .inst_map = &inst_map,345 .inst_map = &inst_map,
305 .target = comp.getTarget(),346 .target = comp.getTarget(),
306 .header = header,347 .header = header,
348 .module = module,
307 };349 };
308 const writer = header.buf.writer();350 const writer = header.buf.writer();
309 renderFunctionSignature(&ctx, writer, decl) catch |err| {351 renderFunctionSignature(&ctx, writer, decl) catch |err| {
...@@ -327,17 +369,15 @@ const Context = struct {...@@ -327,17 +369,15 @@ const Context = struct {
327 error_msg: *Compilation.ErrorMsg = undefined,369 error_msg: *Compilation.ErrorMsg = undefined,
328 target: std.Target,370 target: std.Target,
329 header: *C.Header,371 header: *C.Header,
372 module: *Module,
330373
331 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {374 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
332 if (inst.cast(Inst.Constant)) |const_inst| {375 if (inst.value()) |val| {
333 var out = std.ArrayList(u8).init(&self.arena.allocator);376 var out = std.ArrayList(u8).init(&self.arena.allocator);
334 try renderValue(self, out.writer(), inst.ty, const_inst.val);377 try renderValue(self, out.writer(), inst.ty, val);
335 return out.toOwnedSlice();378 return out.toOwnedSlice();
336 }379 }
337 if (self.inst_map.get(inst)) |val| {380 return self.inst_map.get(inst).?; // Instruction does not dominate all uses!
338 return val;
339 }
340 unreachable;
341 }381 }
342382
343 fn name(self: *Context) ![]u8 {383 fn name(self: *Context) ![]u8 {
...@@ -356,6 +396,27 @@ const Context = struct {...@@ -356,6 +396,27 @@ const Context = struct {
356 }396 }
357};397};
358398
399fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 {
400 const writer = file.main.writer();
401
402 // First line: the variable used as data storage.
403 try indent(file);
404 const local_name = try ctx.name();
405 const elem_type = alloc.base.ty.elemType();
406 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;
407 try renderTypeAndName(ctx, writer, elem_type, local_name, mutability);
408 try writer.writeAll(";\n");
409
410 // Second line: a pointer to it so that we can refer to it as the allocation.
411 // One line for the variable, one line for the pointer to the variable, which we return.
412 try indent(file);
413 const ptr_local_name = try ctx.name();
414 try renderTypeAndName(ctx, writer, alloc.base.ty, ptr_local_name, .Const);
415 try writer.print(" = &{s};\n", .{local_name});
416
417 return ptr_local_name;
418}
419
359fn genArg(ctx: *Context) !?[]u8 {420fn genArg(ctx: *Context) !?[]u8 {
360 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});421 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
361 ctx.argdex += 1;422 ctx.argdex += 1;
...@@ -371,20 +432,10 @@ fn genRetVoid(file: *C) !?[]u8 {...@@ -371,20 +432,10 @@ fn genRetVoid(file: *C) !?[]u8 {
371fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {432fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
372 try indent(file);433 try indent(file);
373 const writer = file.main.writer();434 const writer = file.main.writer();
374 try writer.writeAll("return ");435 try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)});
375 try genValue(ctx, writer, inst.operand);
376 try writer.writeAll(";\n");
377 return null;436 return null;
378}437}
379438
380fn genValue(ctx: *Context, writer: Writer, inst: *Inst) !void {
381 if (inst.value()) |val| {
382 try renderValue(ctx, writer, inst.ty, val);
383 return;
384 }
385 return ctx.fail(ctx.decl.src(), "TODO: C backend: genValue for non-constant value", .{});
386}
387
388fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {439fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
389 if (inst.base.isUnused())440 if (inst.base.isUnused())
390 return null;441 return null;
...@@ -393,25 +444,34 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {...@@ -393,25 +444,34 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
393 const writer = file.main.writer();444 const writer = file.main.writer();
394 const name = try ctx.name();445 const name = try ctx.name();
395 const from = try ctx.resolveInst(inst.operand);446 const from = try ctx.resolveInst(inst.operand);
396 try writer.writeAll("const ");447
397 try renderType(ctx, writer, inst.base.ty);448 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);
398 try writer.print(" {} = (", .{name});449 try writer.writeAll(" = (");
399 try renderType(ctx, writer, inst.base.ty);450 try renderType(ctx, writer, inst.base.ty);
400 try writer.print("){};\n", .{from});451 try writer.print("){s};\n", .{from});
401 return name;452 return name;
402}453}
403454
404fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {455fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 {
456 // *a = b;
457 try indent(file);
458 const writer = file.main.writer();
459 const dest_ptr_name = try ctx.resolveInst(inst.lhs);
460 const src_val_name = try ctx.resolveInst(inst.rhs);
461 try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name });
462 return null;
463}
464
465fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, operator: []const u8) !?[]u8 {
405 if (inst.base.isUnused())466 if (inst.base.isUnused())
406 return null;467 return null;
407 try indent(file);468 try indent(file);
408 const lhs = ctx.resolveInst(inst.lhs);469 const lhs = try ctx.resolveInst(inst.lhs);
409 const rhs = ctx.resolveInst(inst.rhs);470 const rhs = try ctx.resolveInst(inst.rhs);
410 const writer = file.main.writer();471 const writer = file.main.writer();
411 const name = try ctx.name();472 const name = try ctx.name();
412 try writer.writeAll("const ");473 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);
413 try renderType(ctx, writer, inst.base.ty);474 try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs });
414 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
415 return name;475 return name;
416}476}
417477
...@@ -428,13 +488,22 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -428,13 +488,22 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
428 unreachable;488 unreachable;
429489
430 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;490 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
431 const ret_ty = fn_ty.fnReturnType().tag();491 const ret_ty = fn_ty.fnReturnType();
432 if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {492 const unused_result = inst.base.isUnused();
433 try writer.print("(void)", .{});493 var result_name: ?[]u8 = null;
494 if (unused_result) {
495 if (ret_ty.hasCodeGenBits()) {
496 try writer.print("(void)", .{});
497 }
498 } else {
499 const local_name = try ctx.name();
500 try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const);
501 try writer.writeAll(" = ");
502 result_name = local_name;
434 }503 }
435 const fn_name = mem.spanZ(fn_decl.name);504 const fn_name = mem.spanZ(fn_decl.name);
436 if (file.called.get(fn_name) == null) {505 if (file.called.get(fn_name) == null) {
437 try file.called.put(fn_name, void{});506 try file.called.put(fn_name, {});
438 try renderFunctionSignature(ctx, header, fn_decl);507 try renderFunctionSignature(ctx, header, fn_decl);
439 try header.writeAll(";\n");508 try header.writeAll(";\n");
440 }509 }
...@@ -453,10 +522,10 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -453,10 +522,10 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
453 }522 }
454 }523 }
455 try writer.writeAll(");\n");524 try writer.writeAll(");\n");
525 return result_name;
456 } else {526 } else {
457 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});527 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});
458 }528 }
459 return null;
460}529}
461530
462fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {531fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
...@@ -464,6 +533,10 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {...@@ -464,6 +533,10 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
464 return null;533 return null;
465}534}
466535
536fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
537 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
538}
539
467fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {540fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
468 try indent(file);541 try indent(file);
469 try file.main.writer().writeAll("zig_breakpoint();\n");542 try file.main.writer().writeAll("zig_breakpoint();\n");
src/link/C.zig+1-1
...@@ -90,7 +90,7 @@ pub fn deinit(self: *C) void {...@@ -90,7 +90,7 @@ pub fn deinit(self: *C) void {
90}90}
9191
92pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {92pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
93 codegen.generate(self, decl) catch |err| {93 codegen.generate(self, module, decl) catch |err| {
94 if (err == error.AnalysisFail) {94 if (err == error.AnalysisFail) {
95 try module.failed_decls.put(module.gpa, decl, self.error_msg);95 try module.failed_decls.put(module.gpa, decl, self.error_msg);
96 }96 }
src/test.zig+1
...@@ -863,6 +863,7 @@ pub const TestContext = struct {...@@ -863,6 +863,7 @@ pub const TestContext = struct {
863 },863 },
864 }864 }
865 std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);865 std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);
866 std.testing.expectEqualStrings("", exec_result.stderr);
866 },867 },
867 }868 }
868 }869 }
src/zir_sema.zig+5-1
...@@ -352,7 +352,11 @@ fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.Coerc...@@ -352,7 +352,11 @@ fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.Coerc
352}352}
353353
354fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {354fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
355 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});355 const b = try mod.requireFunctionBlock(scope, inst.base.src);
356 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
357 const ret_type = fn_ty.fnReturnType();
358 const ptr_type = try mod.simplePtrType(scope, inst.base.src, ret_type, true, .One);
359 return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
356}360}
357361
358fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {362fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
test/stage2/cbe.zig+41-23
...@@ -33,6 +33,24 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -33,6 +33,24 @@ pub fn addCases(ctx: *TestContext) !void {
33 //, "yo" ++ std.cstr.line_sep);33 //, "yo" ++ std.cstr.line_sep);
34 }34 }
3535
36 {
37 var case = ctx.exeFromCompiledC("alloc and retptr", .{});
38
39 case.addCompareOutput(
40 \\fn add(a: i32, b: i32) i32 {
41 \\ return a + b;
42 \\}
43 \\
44 \\fn addIndirect(a: i32, b: i32) i32 {
45 \\ return add(a, b);
46 \\}
47 \\
48 \\export fn main() c_int {
49 \\ return addIndirect(1, 2) - 3;
50 \\}
51 , "");
52 }
53
36 ctx.c("empty start function", linux_x64,54 ctx.c("empty start function", linux_x64,
37 \\export fn _start() noreturn {55 \\export fn _start() noreturn {
38 \\ unreachable;56 \\ unreachable;
...@@ -59,13 +77,13 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -59,13 +77,13 @@ pub fn addCases(ctx: *TestContext) !void {
59 \\ main();77 \\ main();
60 \\}78 \\}
61 ,79 ,
62 \\zig_noreturn void main(void);80 \\static zig_noreturn void main(void);
63 \\81 \\
64 \\zig_noreturn void _start(void) {82 \\zig_noreturn void _start(void) {
65 \\ main();83 \\ main();
66 \\}84 \\}
67 \\85 \\
68 \\zig_noreturn void main(void) {86 \\static zig_noreturn void main(void) {
69 \\ zig_breakpoint();87 \\ zig_breakpoint();
70 \\ zig_unreachable();88 \\ zig_unreachable();
71 \\}89 \\}
...@@ -87,7 +105,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -87,7 +105,7 @@ pub fn addCases(ctx: *TestContext) !void {
87 \\ exitGood();105 \\ exitGood();
88 \\}106 \\}
89 ,107 ,
90 \\zig_noreturn void exitGood(void);108 \\static zig_noreturn void exitGood(void);
91 \\109 \\
92 \\static uint8_t exitGood__anon_0[6] = "{rax}";110 \\static uint8_t exitGood__anon_0[6] = "{rax}";
93 \\static uint8_t exitGood__anon_1[6] = "{rdi}";111 \\static uint8_t exitGood__anon_1[6] = "{rdi}";
...@@ -97,7 +115,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -97,7 +115,7 @@ pub fn addCases(ctx: *TestContext) !void {
97 \\ exitGood();115 \\ exitGood();
98 \\}116 \\}
99 \\117 \\
100 \\zig_noreturn void exitGood(void) {118 \\static zig_noreturn void exitGood(void) {
101 \\ register uintptr_t rax_constant __asm__("rax") = 231;119 \\ register uintptr_t rax_constant __asm__("rax") = 231;
102 \\ register uintptr_t rdi_constant __asm__("rdi") = 0;120 \\ register uintptr_t rdi_constant __asm__("rdi") = 0;
103 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));121 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
...@@ -121,7 +139,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -121,7 +139,7 @@ pub fn addCases(ctx: *TestContext) !void {
121 \\}139 \\}
122 \\140 \\
123 ,141 ,
124 \\zig_noreturn void exit(uintptr_t arg0);142 \\static zig_noreturn void exit(uintptr_t arg0);
125 \\143 \\
126 \\static uint8_t exit__anon_0[6] = "{rax}";144 \\static uint8_t exit__anon_0[6] = "{rax}";
127 \\static uint8_t exit__anon_1[6] = "{rdi}";145 \\static uint8_t exit__anon_1[6] = "{rdi}";
...@@ -131,7 +149,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -131,7 +149,7 @@ pub fn addCases(ctx: *TestContext) !void {
131 \\ exit(0);149 \\ exit(0);
132 \\}150 \\}
133 \\151 \\
134 \\zig_noreturn void exit(uintptr_t arg0) {152 \\static zig_noreturn void exit(uintptr_t arg0) {
135 \\ register uintptr_t rax_constant __asm__("rax") = 231;153 \\ register uintptr_t rax_constant __asm__("rax") = 231;
136 \\ register uintptr_t rdi_constant __asm__("rdi") = arg0;154 \\ register uintptr_t rdi_constant __asm__("rdi") = arg0;
137 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));155 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
...@@ -155,7 +173,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -155,7 +173,7 @@ pub fn addCases(ctx: *TestContext) !void {
155 \\}173 \\}
156 \\174 \\
157 ,175 ,
158 \\zig_noreturn void exit(uint8_t arg0);176 \\static zig_noreturn void exit(uint8_t arg0);
159 \\177 \\
160 \\static uint8_t exit__anon_0[6] = "{rax}";178 \\static uint8_t exit__anon_0[6] = "{rax}";
161 \\static uint8_t exit__anon_1[6] = "{rdi}";179 \\static uint8_t exit__anon_1[6] = "{rdi}";
...@@ -165,8 +183,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -165,8 +183,8 @@ pub fn addCases(ctx: *TestContext) !void {
165 \\ exit(0);183 \\ exit(0);
166 \\}184 \\}
167 \\185 \\
168 \\zig_noreturn void exit(uint8_t arg0) {186 \\static zig_noreturn void exit(uint8_t arg0) {
169 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;187 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
170 \\ register uintptr_t rax_constant __asm__("rax") = 231;188 \\ register uintptr_t rax_constant __asm__("rax") = 231;
171 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;189 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
172 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));190 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
...@@ -194,8 +212,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -194,8 +212,8 @@ pub fn addCases(ctx: *TestContext) !void {
194 \\}212 \\}
195 \\213 \\
196 ,214 ,
197 \\zig_noreturn void exitMath(uint8_t arg0);215 \\static zig_noreturn void exitMath(uint8_t arg0);
198 \\zig_noreturn void exit(uint8_t arg0);216 \\static zig_noreturn void exit(uint8_t arg0);
199 \\217 \\
200 \\static uint8_t exit__anon_0[6] = "{rax}";218 \\static uint8_t exit__anon_0[6] = "{rax}";
201 \\static uint8_t exit__anon_1[6] = "{rdi}";219 \\static uint8_t exit__anon_1[6] = "{rdi}";
...@@ -205,14 +223,14 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -205,14 +223,14 @@ pub fn addCases(ctx: *TestContext) !void {
205 \\ exitMath(1);223 \\ exitMath(1);
206 \\}224 \\}
207 \\225 \\
208 \\zig_noreturn void exitMath(uint8_t arg0) {226 \\static zig_noreturn void exitMath(uint8_t arg0) {
209 \\ const uint8_t __temp_0 = 0 + arg0;227 \\ uint8_t const __temp_0 = 0 + arg0;
210 \\ const uint8_t __temp_1 = __temp_0 - arg0;228 \\ uint8_t const __temp_1 = __temp_0 - arg0;
211 \\ exit(__temp_1);229 \\ exit(__temp_1);
212 \\}230 \\}
213 \\231 \\
214 \\zig_noreturn void exit(uint8_t arg0) {232 \\static zig_noreturn void exit(uint8_t arg0) {
215 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;233 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
216 \\ register uintptr_t rax_constant __asm__("rax") = 231;234 \\ register uintptr_t rax_constant __asm__("rax") = 231;
217 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;235 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
218 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));236 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
...@@ -240,8 +258,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -240,8 +258,8 @@ pub fn addCases(ctx: *TestContext) !void {
240 \\}258 \\}
241 \\259 \\
242 ,260 ,
243 \\zig_noreturn void exitMath(uint8_t arg0);261 \\static zig_noreturn void exitMath(uint8_t arg0);
244 \\zig_noreturn void exit(uint8_t arg0);262 \\static zig_noreturn void exit(uint8_t arg0);
245 \\263 \\
246 \\static uint8_t exit__anon_0[6] = "{rax}";264 \\static uint8_t exit__anon_0[6] = "{rax}";
247 \\static uint8_t exit__anon_1[6] = "{rdi}";265 \\static uint8_t exit__anon_1[6] = "{rdi}";
...@@ -251,14 +269,14 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -251,14 +269,14 @@ pub fn addCases(ctx: *TestContext) !void {
251 \\ exitMath(1);269 \\ exitMath(1);
252 \\}270 \\}
253 \\271 \\
254 \\zig_noreturn void exitMath(uint8_t arg0) {272 \\static zig_noreturn void exitMath(uint8_t arg0) {
255 \\ const uint8_t __temp_0 = arg0 + 0;273 \\ uint8_t const __temp_0 = arg0 + 0;
256 \\ const uint8_t __temp_1 = __temp_0 - arg0;274 \\ uint8_t const __temp_1 = __temp_0 - arg0;
257 \\ exit(__temp_1);275 \\ exit(__temp_1);
258 \\}276 \\}
259 \\277 \\
260 \\zig_noreturn void exit(uint8_t arg0) {278 \\static zig_noreturn void exit(uint8_t arg0) {
261 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;279 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
262 \\ register uintptr_t rax_constant __asm__("rax") = 231;280 \\ register uintptr_t rax_constant __asm__("rax") = 231;
263 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;281 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
264 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));282 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));