| ... | ... | @@ -21,6 +21,34 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { |
| 21 | 21 | return allocator.dupe(u8, name); |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | const Mutability = enum { Const, Mut }; |
| 25 | |
| 26 | fn 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 | |
| 24 | 52 | fn renderType( |
| 25 | 53 | ctx: *Context, |
| 26 | 54 | writer: Writer, |
| ... | ... | @@ -74,14 +102,14 @@ fn renderType( |
| 74 | 102 | if (t.isSlice()) { |
| 75 | 103 | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{}); |
| 76 | 104 | } else { |
| 105 | try renderType(ctx, writer, t.elemType()); |
| 106 | try writer.writeAll(" *"); |
| 77 | 107 | if (t.isConstPtr()) { |
| 78 | 108 | try writer.writeAll("const "); |
| 79 | 109 | } |
| 80 | 110 | if (t.isVolatilePtr()) { |
| 81 | 111 | try writer.writeAll("volatile "); |
| 82 | 112 | } |
| 83 | | try renderType(ctx, writer, t.elemType()); |
| 84 | | try writer.writeAll(" *"); |
| 85 | 113 | } |
| 86 | 114 | }, |
| 87 | 115 | .Array => { |
| ... | ... | @@ -176,12 +204,27 @@ fn renderFunctionSignature( |
| 176 | 204 | decl: *Decl, |
| 177 | 205 | ) !void { |
| 178 | 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 | 221 | try renderType(ctx, writer, tv.ty.fnReturnType()); |
| 180 | 222 | // Use the child allocator directly, as we know the name can be freed before |
| 181 | 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 | 226 | defer ctx.arena.child_allocator.free(name); |
| 184 | | try writer.print(" {}(", .{name}); |
| 227 | try writer.print(" {s}(", .{name}); |
| 185 | 228 | var param_len = tv.ty.fnParamLen(); |
| 186 | 229 | if (param_len == 0) |
| 187 | 230 | try writer.writeAll("void") |
| ... | ... | @@ -205,7 +248,7 @@ fn indent(file: *C) !void { |
| 205 | 248 | try file.main.writer().writeByteNTimes(' ', indent_amt); |
| 206 | 249 | } |
| 207 | 250 | |
| 208 | | pub fn generate(file: *C, decl: *Decl) !void { |
| 251 | pub fn generate(file: *C, module: *Module, decl: *Decl) !void { |
| 209 | 252 | const tv = decl.typed_value.most_recent.typed_value; |
| 210 | 253 | |
| 211 | 254 | var arena = std.heap.ArenaAllocator.init(file.base.allocator); |
| ... | ... | @@ -218,6 +261,7 @@ pub fn generate(file: *C, decl: *Decl) !void { |
| 218 | 261 | .inst_map = &inst_map, |
| 219 | 262 | .target = file.base.options.target, |
| 220 | 263 | .header = &file.header, |
| 264 | .module = module, |
| 221 | 265 | }; |
| 222 | 266 | defer { |
| 223 | 267 | file.error_msg = ctx.error_msg; |
| ... | ... | @@ -236,17 +280,26 @@ pub fn generate(file: *C, decl: *Decl) !void { |
| 236 | 280 | try writer.writeAll("\n"); |
| 237 | 281 | for (instructions) |inst| { |
| 238 | 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 | 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 | 289 | .call => try genCall(&ctx, file, inst.castTag(.call).?), |
| 241 | | .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"), |
| 242 | | .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"), |
| 290 | .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="), |
| 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 | 298 | .ret => try genRet(&ctx, file, inst.castTag(.ret).?), |
| 244 | 299 | .retvoid => try genRetVoid(file), |
| 245 | | .arg => try genArg(&ctx), |
| 246 | | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), |
| 247 | | .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?), |
| 300 | .store => try genStore(&ctx, file, inst.castTag(.store).?), |
| 301 | .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"), |
| 248 | 302 | .unreach => try genUnreach(file, inst.castTag(.unreach).?), |
| 249 | | .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?), |
| 250 | 303 | else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}), |
| 251 | 304 | }) |name| { |
| 252 | 305 | try ctx.inst_map.putNoClobber(inst, name); |
| ... | ... | @@ -264,19 +317,7 @@ pub fn generate(file: *C, decl: *Decl) !void { |
| 264 | 317 | // TODO ask the Decl if it is const |
| 265 | 318 | // https://github.com/ziglang/zig/issues/7582 |
| 266 | 319 | |
| 267 | | var suffix = std.ArrayList(u8).init(file.base.allocator); |
| 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 }); |
| 320 | try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut); |
| 280 | 321 | |
| 281 | 322 | try writer.writeAll(" = "); |
| 282 | 323 | try renderValue(&ctx, writer, tv.ty, tv.val); |
| ... | ... | @@ -304,6 +345,7 @@ pub fn generateHeader( |
| 304 | 345 | .inst_map = &inst_map, |
| 305 | 346 | .target = comp.getTarget(), |
| 306 | 347 | .header = header, |
| 348 | .module = module, |
| 307 | 349 | }; |
| 308 | 350 | const writer = header.buf.writer(); |
| 309 | 351 | renderFunctionSignature(&ctx, writer, decl) catch |err| { |
| ... | ... | @@ -327,17 +369,15 @@ const Context = struct { |
| 327 | 369 | error_msg: *Compilation.ErrorMsg = undefined, |
| 328 | 370 | target: std.Target, |
| 329 | 371 | header: *C.Header, |
| 372 | module: *Module, |
| 330 | 373 | |
| 331 | 374 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| 332 | | if (inst.cast(Inst.Constant)) |const_inst| { |
| 375 | if (inst.value()) |val| { |
| 333 | 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 | 378 | return out.toOwnedSlice(); |
| 336 | 379 | } |
| 337 | | if (self.inst_map.get(inst)) |val| { |
| 338 | | return val; |
| 339 | | } |
| 340 | | unreachable; |
| 380 | return self.inst_map.get(inst).?; // Instruction does not dominate all uses! |
| 341 | 381 | } |
| 342 | 382 | |
| 343 | 383 | fn name(self: *Context) ![]u8 { |
| ... | ... | @@ -356,6 +396,27 @@ const Context = struct { |
| 356 | 396 | } |
| 357 | 397 | }; |
| 358 | 398 | |
| 399 | fn 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 | |
| 359 | 420 | fn genArg(ctx: *Context) !?[]u8 { |
| 360 | 421 | const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); |
| 361 | 422 | ctx.argdex += 1; |
| ... | ... | @@ -371,20 +432,10 @@ fn genRetVoid(file: *C) !?[]u8 { |
| 371 | 432 | fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 372 | 433 | try indent(file); |
| 373 | 434 | const writer = file.main.writer(); |
| 374 | | try writer.writeAll("return "); |
| 375 | | try genValue(ctx, writer, inst.operand); |
| 376 | | try writer.writeAll(";\n"); |
| 435 | try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)}); |
| 377 | 436 | return null; |
| 378 | 437 | } |
| 379 | 438 | |
| 380 | | fn 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 | | |
| 388 | 439 | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 389 | 440 | if (inst.base.isUnused()) |
| 390 | 441 | return null; |
| ... | ... | @@ -393,25 +444,34 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 393 | 444 | const writer = file.main.writer(); |
| 394 | 445 | const name = try ctx.name(); |
| 395 | 446 | const from = try ctx.resolveInst(inst.operand); |
| 396 | | try writer.writeAll("const "); |
| 397 | | try renderType(ctx, writer, inst.base.ty); |
| 398 | | try writer.print(" {} = (", .{name}); |
| 447 | |
| 448 | try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const); |
| 449 | try writer.writeAll(" = ("); |
| 399 | 450 | try renderType(ctx, writer, inst.base.ty); |
| 400 | | try writer.print("){};\n", .{from}); |
| 451 | try writer.print("){s};\n", .{from}); |
| 401 | 452 | return name; |
| 402 | 453 | } |
| 403 | 454 | |
| 404 | | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { |
| 455 | fn 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 | |
| 465 | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, operator: []const u8) !?[]u8 { |
| 405 | 466 | if (inst.base.isUnused()) |
| 406 | 467 | return null; |
| 407 | 468 | try indent(file); |
| 408 | | const lhs = ctx.resolveInst(inst.lhs); |
| 409 | | const rhs = ctx.resolveInst(inst.rhs); |
| 469 | const lhs = try ctx.resolveInst(inst.lhs); |
| 470 | const rhs = try ctx.resolveInst(inst.rhs); |
| 410 | 471 | const writer = file.main.writer(); |
| 411 | 472 | const name = try ctx.name(); |
| 412 | | try writer.writeAll("const "); |
| 413 | | try renderType(ctx, writer, inst.base.ty); |
| 414 | | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); |
| 473 | try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const); |
| 474 | try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs }); |
| 415 | 475 | return name; |
| 416 | 476 | } |
| 417 | 477 | |
| ... | ... | @@ -428,13 +488,22 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 428 | 488 | unreachable; |
| 429 | 489 | |
| 430 | 490 | const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty; |
| 431 | | const ret_ty = fn_ty.fnReturnType().tag(); |
| 432 | | if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) { |
| 433 | | try writer.print("(void)", .{}); |
| 491 | const ret_ty = fn_ty.fnReturnType(); |
| 492 | const unused_result = inst.base.isUnused(); |
| 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 | 504 | const fn_name = mem.spanZ(fn_decl.name); |
| 436 | 505 | if (file.called.get(fn_name) == null) { |
| 437 | | try file.called.put(fn_name, void{}); |
| 506 | try file.called.put(fn_name, {}); |
| 438 | 507 | try renderFunctionSignature(ctx, header, fn_decl); |
| 439 | 508 | try header.writeAll(";\n"); |
| 440 | 509 | } |
| ... | ... | @@ -453,10 +522,10 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 453 | 522 | } |
| 454 | 523 | } |
| 455 | 524 | try writer.writeAll(");\n"); |
| 525 | return result_name; |
| 456 | 526 | } else { |
| 457 | 527 | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{}); |
| 458 | 528 | } |
| 459 | | return null; |
| 460 | 529 | } |
| 461 | 530 | |
| 462 | 531 | fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| ... | ... | @@ -464,6 +533,10 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 464 | 533 | return null; |
| 465 | 534 | } |
| 466 | 535 | |
| 536 | fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 { |
| 537 | return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{}); |
| 538 | } |
| 539 | |
| 467 | 540 | fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 { |
| 468 | 541 | try indent(file); |
| 469 | 542 | try file.main.writer().writeAll("zig_breakpoint();\n"); |