| ... | @@ -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 | } |
| 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 | fn renderType( | 52 | fn 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 before | 222 | // 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 | } |
| 207 | | 250 | |
| 208 | pub fn generate(file: *C, decl: *Decl) !void { | 251 | pub 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; |
| 210 | | 253 | |
| 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 const | 317 | // TODO ask the Decl if it is const |
| 265 | // https://github.com/ziglang/zig/issues/7582 | 318 | // https://github.com/ziglang/zig/issues/7582 |
| 266 | | 319 | |
| 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 }); | | |
| 280 | | 321 | |
| 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, |
| 330 | | 373 | |
| 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 | } |
| 342 | | 382 | |
| 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 | }; |
| 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 | fn genArg(ctx: *Context) !?[]u8 { | 420 | fn 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 { |
| 371 | fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { | 432 | fn 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 | } |
| 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 | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { | 439 | fn 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 | } |
| 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 | 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 | } |
| 417 | | 477 | |
| ... | @@ -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; |
| 429 | | 489 | |
| 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 | } |
| 461 | | 530 | |
| 462 | fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | 531 | fn 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 | } |
| 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 | fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 { | 540 | fn 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"); |