mirror of
https://github.com/ziglang/zig.git
synced 2025-12-25 15:43:06 +00:00
Instead of freeing ZIR after semantic analysis, we keep it around so that it can be used for comptime calls, inline calls, and generic function calls. ZIR memory is now managed by the Decl arena. Debug dump() functions are conditionally compiled; only available in Debug builds of the compiler. Add a test for an inline function call.
632 lines
23 KiB
Zig
632 lines
23 KiB
Zig
const std = @import("std");
|
|
|
|
const link = @import("../link.zig");
|
|
const Module = @import("../Module.zig");
|
|
const Compilation = @import("../Compilation.zig");
|
|
|
|
const Inst = @import("../ir.zig").Inst;
|
|
const Value = @import("../value.zig").Value;
|
|
const Type = @import("../type.zig").Type;
|
|
|
|
const C = link.File.C;
|
|
const Decl = Module.Decl;
|
|
const mem = std.mem;
|
|
const log = std.log.scoped(.c);
|
|
|
|
const Writer = std.ArrayList(u8).Writer;
|
|
|
|
/// Maps a name from Zig source to C. Currently, this will always give the same
|
|
/// output for any given input, sometimes resulting in broken identifiers.
|
|
fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
|
|
return allocator.dupe(u8, name);
|
|
}
|
|
|
|
const Mutability = enum { Const, Mut };
|
|
|
|
fn renderTypeAndName(
|
|
ctx: *Context,
|
|
writer: Writer,
|
|
ty: Type,
|
|
name: []const u8,
|
|
mutability: Mutability,
|
|
) error{ OutOfMemory, AnalysisFail }!void {
|
|
var suffix = std.ArrayList(u8).init(&ctx.arena.allocator);
|
|
|
|
var render_ty = ty;
|
|
while (render_ty.zigTypeTag() == .Array) {
|
|
const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
|
|
const c_len = render_ty.arrayLen() + sentinel_bit;
|
|
try suffix.writer().print("[{d}]", .{c_len});
|
|
render_ty = render_ty.elemType();
|
|
}
|
|
|
|
try renderType(ctx, writer, render_ty);
|
|
|
|
const const_prefix = switch (mutability) {
|
|
.Const => "const ",
|
|
.Mut => "",
|
|
};
|
|
try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items });
|
|
}
|
|
|
|
fn renderType(
|
|
ctx: *Context,
|
|
writer: Writer,
|
|
t: Type,
|
|
) error{ OutOfMemory, AnalysisFail }!void {
|
|
switch (t.zigTypeTag()) {
|
|
.NoReturn => {
|
|
try writer.writeAll("zig_noreturn void");
|
|
},
|
|
.Void => try writer.writeAll("void"),
|
|
.Bool => try writer.writeAll("bool"),
|
|
.Int => {
|
|
switch (t.tag()) {
|
|
.u8 => try writer.writeAll("uint8_t"),
|
|
.i8 => try writer.writeAll("int8_t"),
|
|
.u16 => try writer.writeAll("uint16_t"),
|
|
.i16 => try writer.writeAll("int16_t"),
|
|
.u32 => try writer.writeAll("uint32_t"),
|
|
.i32 => try writer.writeAll("int32_t"),
|
|
.u64 => try writer.writeAll("uint64_t"),
|
|
.i64 => try writer.writeAll("int64_t"),
|
|
.usize => try writer.writeAll("uintptr_t"),
|
|
.isize => try writer.writeAll("intptr_t"),
|
|
.c_short => try writer.writeAll("short"),
|
|
.c_ushort => try writer.writeAll("unsigned short"),
|
|
.c_int => try writer.writeAll("int"),
|
|
.c_uint => try writer.writeAll("unsigned int"),
|
|
.c_long => try writer.writeAll("long"),
|
|
.c_ulong => try writer.writeAll("unsigned long"),
|
|
.c_longlong => try writer.writeAll("long long"),
|
|
.c_ulonglong => try writer.writeAll("unsigned long long"),
|
|
.int_signed, .int_unsigned => {
|
|
const info = t.intInfo(ctx.target);
|
|
const sign_prefix = switch (info.signedness) {
|
|
.signed => "i",
|
|
.unsigned => "",
|
|
};
|
|
inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
|
|
if (info.bits <= nbits) {
|
|
try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });
|
|
break;
|
|
}
|
|
} else {
|
|
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
.Pointer => {
|
|
if (t.isSlice()) {
|
|
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});
|
|
} else {
|
|
try renderType(ctx, writer, t.elemType());
|
|
try writer.writeAll(" *");
|
|
if (t.isConstPtr()) {
|
|
try writer.writeAll("const ");
|
|
}
|
|
if (t.isVolatilePtr()) {
|
|
try writer.writeAll("volatile ");
|
|
}
|
|
}
|
|
},
|
|
.Array => {
|
|
try renderType(ctx, writer, t.elemType());
|
|
try writer.writeAll(" *");
|
|
},
|
|
else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement type {s}", .{
|
|
@tagName(e),
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn renderValue(
|
|
ctx: *Context,
|
|
writer: Writer,
|
|
t: Type,
|
|
val: Value,
|
|
) error{ OutOfMemory, AnalysisFail }!void {
|
|
switch (t.zigTypeTag()) {
|
|
.Int => {
|
|
if (t.isSignedInt())
|
|
return writer.print("{d}", .{val.toSignedInt()});
|
|
return writer.print("{d}", .{val.toUnsignedInt()});
|
|
},
|
|
.Pointer => switch (val.tag()) {
|
|
.undef, .zero => try writer.writeAll("0"),
|
|
.one => try writer.writeAll("1"),
|
|
.decl_ref => {
|
|
const decl = val.castTag(.decl_ref).?.data;
|
|
|
|
// Determine if we must pointer cast.
|
|
const decl_tv = decl.typed_value.most_recent.typed_value;
|
|
if (t.eql(decl_tv.ty)) {
|
|
try writer.print("&{s}", .{decl.name});
|
|
} else {
|
|
try writer.writeAll("(");
|
|
try renderType(ctx, writer, t);
|
|
try writer.print(")&{s}", .{decl.name});
|
|
}
|
|
},
|
|
.function => {
|
|
const func = val.castTag(.function).?.data;
|
|
try writer.print("{s}", .{func.owner_decl.name});
|
|
},
|
|
.extern_fn => {
|
|
const decl = val.castTag(.extern_fn).?.data;
|
|
try writer.print("{s}", .{decl.name});
|
|
},
|
|
else => |e| return ctx.fail(
|
|
ctx.decl.src(),
|
|
"TODO: C backend: implement Pointer value {s}",
|
|
.{@tagName(e)},
|
|
),
|
|
},
|
|
.Array => {
|
|
// First try specific tag representations for more efficiency.
|
|
switch (val.tag()) {
|
|
.undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
|
|
.bytes => {
|
|
const bytes = val.castTag(.bytes).?.data;
|
|
// TODO: make our own C string escape instead of using {Z}
|
|
try writer.print("\"{Z}\"", .{bytes});
|
|
},
|
|
else => {
|
|
// Fall back to generic implementation.
|
|
try writer.writeAll("{");
|
|
var index: usize = 0;
|
|
const len = t.arrayLen();
|
|
const elem_ty = t.elemType();
|
|
while (index < len) : (index += 1) {
|
|
if (index != 0) try writer.writeAll(",");
|
|
const elem_val = try val.elemValue(&ctx.arena.allocator, index);
|
|
try renderValue(ctx, writer, elem_ty, elem_val);
|
|
}
|
|
if (t.sentinel()) |sentinel_val| {
|
|
if (index != 0) try writer.writeAll(",");
|
|
try renderValue(ctx, writer, elem_ty, sentinel_val);
|
|
}
|
|
try writer.writeAll("}");
|
|
},
|
|
}
|
|
},
|
|
else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{
|
|
@tagName(e),
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn renderFunctionSignature(
|
|
ctx: *Context,
|
|
writer: Writer,
|
|
decl: *Decl,
|
|
) !void {
|
|
const tv = decl.typed_value.most_recent.typed_value;
|
|
// Determine whether the function is globally visible.
|
|
const is_global = blk: {
|
|
switch (tv.val.tag()) {
|
|
.extern_fn => break :blk true,
|
|
.function => {
|
|
const func = tv.val.castTag(.function).?.data;
|
|
break :blk ctx.module.decl_exports.contains(func.owner_decl);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
};
|
|
if (!is_global) {
|
|
try writer.writeAll("static ");
|
|
}
|
|
try renderType(ctx, writer, tv.ty.fnReturnType());
|
|
// Use the child allocator directly, as we know the name can be freed before
|
|
// the rest of the arena.
|
|
const decl_name = mem.span(decl.name);
|
|
const name = try map(ctx.arena.child_allocator, decl_name);
|
|
defer ctx.arena.child_allocator.free(name);
|
|
try writer.print(" {s}(", .{name});
|
|
var param_len = tv.ty.fnParamLen();
|
|
if (param_len == 0)
|
|
try writer.writeAll("void")
|
|
else {
|
|
var index: usize = 0;
|
|
while (index < param_len) : (index += 1) {
|
|
if (index > 0) {
|
|
try writer.writeAll(", ");
|
|
}
|
|
try renderType(ctx, writer, tv.ty.fnParamType(index));
|
|
try writer.print(" arg{d}", .{index});
|
|
}
|
|
}
|
|
try writer.writeByte(')');
|
|
}
|
|
|
|
fn indent(file: *C) !void {
|
|
const indent_size = 4;
|
|
const indent_level = 1;
|
|
const indent_amt = indent_size * indent_level;
|
|
try file.main.writer().writeByteNTimes(' ', indent_amt);
|
|
}
|
|
|
|
pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
|
|
const tv = decl.typed_value.most_recent.typed_value;
|
|
|
|
var arena = std.heap.ArenaAllocator.init(file.base.allocator);
|
|
defer arena.deinit();
|
|
var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
|
|
defer inst_map.deinit();
|
|
var ctx = Context{
|
|
.decl = decl,
|
|
.arena = &arena,
|
|
.inst_map = &inst_map,
|
|
.target = file.base.options.target,
|
|
.header = &file.header,
|
|
.module = module,
|
|
};
|
|
defer {
|
|
file.error_msg = ctx.error_msg;
|
|
ctx.deinit();
|
|
}
|
|
|
|
if (tv.val.castTag(.function)) |func_payload| {
|
|
const writer = file.main.writer();
|
|
try renderFunctionSignature(&ctx, writer, decl);
|
|
|
|
try writer.writeAll(" {");
|
|
|
|
const func: *Module.Fn = func_payload.data;
|
|
const instructions = func.body.instructions;
|
|
if (instructions.len > 0) {
|
|
try writer.writeAll("\n");
|
|
for (instructions) |inst| {
|
|
if (switch (inst.tag) {
|
|
.add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"),
|
|
.alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?),
|
|
.arg => try genArg(&ctx),
|
|
.assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
|
|
.block => try genBlock(&ctx, file, inst.castTag(.block).?),
|
|
.bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),
|
|
.breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
|
|
.call => try genCall(&ctx, file, inst.castTag(.call).?),
|
|
.cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
|
|
.cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"),
|
|
.cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="),
|
|
.cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"),
|
|
.cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="),
|
|
.cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
|
|
.dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
|
|
.intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
|
|
.load => try genLoad(&ctx, file, inst.castTag(.load).?),
|
|
.ret => try genRet(&ctx, file, inst.castTag(.ret).?),
|
|
.retvoid => try genRetVoid(file),
|
|
.store => try genStore(&ctx, file, inst.castTag(.store).?),
|
|
.sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"),
|
|
.unreach => try genUnreach(file, inst.castTag(.unreach).?),
|
|
else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
|
|
}) |name| {
|
|
try ctx.inst_map.putNoClobber(inst, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
try writer.writeAll("}\n\n");
|
|
} else if (tv.val.tag() == .extern_fn) {
|
|
return; // handled when referenced
|
|
} else {
|
|
const writer = file.constants.writer();
|
|
try writer.writeAll("static ");
|
|
|
|
// TODO ask the Decl if it is const
|
|
// https://github.com/ziglang/zig/issues/7582
|
|
|
|
try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut);
|
|
|
|
try writer.writeAll(" = ");
|
|
try renderValue(&ctx, writer, tv.ty, tv.val);
|
|
try writer.writeAll(";\n");
|
|
}
|
|
}
|
|
|
|
pub fn generateHeader(
|
|
comp: *Compilation,
|
|
module: *Module,
|
|
header: *C.Header,
|
|
decl: *Decl,
|
|
) error{ AnalysisFail, OutOfMemory }!void {
|
|
switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
|
|
.Fn => {
|
|
var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
|
|
defer inst_map.deinit();
|
|
|
|
var arena = std.heap.ArenaAllocator.init(comp.gpa);
|
|
defer arena.deinit();
|
|
|
|
var ctx = Context{
|
|
.decl = decl,
|
|
.arena = &arena,
|
|
.inst_map = &inst_map,
|
|
.target = comp.getTarget(),
|
|
.header = header,
|
|
.module = module,
|
|
};
|
|
const writer = header.buf.writer();
|
|
renderFunctionSignature(&ctx, writer, decl) catch |err| {
|
|
if (err == error.AnalysisFail) {
|
|
try module.failed_decls.put(module.gpa, decl, ctx.error_msg);
|
|
}
|
|
return err;
|
|
};
|
|
try writer.writeAll(";\n");
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
const Context = struct {
|
|
decl: *Decl,
|
|
inst_map: *std.AutoHashMap(*Inst, []u8),
|
|
arena: *std.heap.ArenaAllocator,
|
|
argdex: usize = 0,
|
|
unnamed_index: usize = 0,
|
|
error_msg: *Compilation.ErrorMsg = undefined,
|
|
target: std.Target,
|
|
header: *C.Header,
|
|
module: *Module,
|
|
|
|
fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
|
|
if (inst.value()) |val| {
|
|
var out = std.ArrayList(u8).init(&self.arena.allocator);
|
|
try renderValue(self, out.writer(), inst.ty, val);
|
|
return out.toOwnedSlice();
|
|
}
|
|
return self.inst_map.get(inst).?; // Instruction does not dominate all uses!
|
|
}
|
|
|
|
fn name(self: *Context) ![]u8 {
|
|
const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{d}", .{self.unnamed_index});
|
|
self.unnamed_index += 1;
|
|
return val;
|
|
}
|
|
|
|
fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
|
|
self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args);
|
|
return error.AnalysisFail;
|
|
}
|
|
|
|
fn deinit(self: *Context) void {
|
|
self.* = undefined;
|
|
}
|
|
};
|
|
|
|
fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 {
|
|
const writer = file.main.writer();
|
|
|
|
// First line: the variable used as data storage.
|
|
try indent(file);
|
|
const local_name = try ctx.name();
|
|
const elem_type = alloc.base.ty.elemType();
|
|
const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;
|
|
try renderTypeAndName(ctx, writer, elem_type, local_name, mutability);
|
|
try writer.writeAll(";\n");
|
|
|
|
// Second line: a pointer to it so that we can refer to it as the allocation.
|
|
// One line for the variable, one line for the pointer to the variable, which we return.
|
|
try indent(file);
|
|
const ptr_local_name = try ctx.name();
|
|
try renderTypeAndName(ctx, writer, alloc.base.ty, ptr_local_name, .Const);
|
|
try writer.print(" = &{s};\n", .{local_name});
|
|
|
|
return ptr_local_name;
|
|
}
|
|
|
|
fn genArg(ctx: *Context) !?[]u8 {
|
|
const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex});
|
|
ctx.argdex += 1;
|
|
return name;
|
|
}
|
|
|
|
fn genRetVoid(file: *C) !?[]u8 {
|
|
try indent(file);
|
|
try file.main.writer().print("return;\n", .{});
|
|
return null;
|
|
}
|
|
|
|
fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
|
const operand = try ctx.resolveInst(inst.operand);
|
|
const writer = file.main.writer();
|
|
try indent(file);
|
|
const local_name = try ctx.name();
|
|
try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
|
|
try writer.print(" = *{s};\n", .{operand});
|
|
return local_name;
|
|
}
|
|
|
|
fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
|
try indent(file);
|
|
const writer = file.main.writer();
|
|
try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)});
|
|
return null;
|
|
}
|
|
|
|
fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
|
if (inst.base.isUnused())
|
|
return null;
|
|
try indent(file);
|
|
const writer = file.main.writer();
|
|
const name = try ctx.name();
|
|
const from = try ctx.resolveInst(inst.operand);
|
|
|
|
try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);
|
|
try writer.writeAll(" = (");
|
|
try renderType(ctx, writer, inst.base.ty);
|
|
try writer.print("){s};\n", .{from});
|
|
return name;
|
|
}
|
|
|
|
fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 {
|
|
// *a = b;
|
|
try indent(file);
|
|
const writer = file.main.writer();
|
|
const dest_ptr_name = try ctx.resolveInst(inst.lhs);
|
|
const src_val_name = try ctx.resolveInst(inst.rhs);
|
|
try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name });
|
|
return null;
|
|
}
|
|
|
|
fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, operator: []const u8) !?[]u8 {
|
|
if (inst.base.isUnused())
|
|
return null;
|
|
try indent(file);
|
|
const lhs = try ctx.resolveInst(inst.lhs);
|
|
const rhs = try ctx.resolveInst(inst.rhs);
|
|
const writer = file.main.writer();
|
|
const name = try ctx.name();
|
|
try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);
|
|
try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs });
|
|
return name;
|
|
}
|
|
|
|
fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
|
|
try indent(file);
|
|
const writer = file.main.writer();
|
|
const header = file.header.buf.writer();
|
|
if (inst.func.castTag(.constant)) |func_inst| {
|
|
const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|
|
|
extern_fn.data
|
|
else if (func_inst.val.castTag(.function)) |func_payload|
|
|
func_payload.data.owner_decl
|
|
else
|
|
unreachable;
|
|
|
|
const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
|
|
const ret_ty = fn_ty.fnReturnType();
|
|
const unused_result = inst.base.isUnused();
|
|
var result_name: ?[]u8 = null;
|
|
if (unused_result) {
|
|
if (ret_ty.hasCodeGenBits()) {
|
|
try writer.print("(void)", .{});
|
|
}
|
|
} else {
|
|
const local_name = try ctx.name();
|
|
try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const);
|
|
try writer.writeAll(" = ");
|
|
result_name = local_name;
|
|
}
|
|
const fn_name = mem.spanZ(fn_decl.name);
|
|
if (file.called.get(fn_name) == null) {
|
|
try file.called.put(fn_name, {});
|
|
try renderFunctionSignature(ctx, header, fn_decl);
|
|
try header.writeAll(";\n");
|
|
}
|
|
try writer.print("{s}(", .{fn_name});
|
|
if (inst.args.len != 0) {
|
|
for (inst.args) |arg, i| {
|
|
if (i > 0) {
|
|
try writer.writeAll(", ");
|
|
}
|
|
if (arg.value()) |val| {
|
|
try renderValue(ctx, writer, arg.ty, val);
|
|
} else {
|
|
const val = try ctx.resolveInst(arg);
|
|
try writer.print("{s}", .{val});
|
|
}
|
|
}
|
|
}
|
|
try writer.writeAll(");\n");
|
|
return result_name;
|
|
} else {
|
|
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});
|
|
}
|
|
}
|
|
|
|
fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
|
|
// TODO emit #line directive here with line number and filename
|
|
return null;
|
|
}
|
|
|
|
fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
|
|
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
|
|
}
|
|
|
|
fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
|
const writer = file.main.writer();
|
|
try indent(file);
|
|
const local_name = try ctx.name();
|
|
const operand = try ctx.resolveInst(inst.operand);
|
|
try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
|
|
if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
|
|
try writer.writeAll(" = (");
|
|
try renderType(ctx, writer, inst.base.ty);
|
|
try writer.print("){s};\n", .{operand});
|
|
} else {
|
|
try writer.writeAll(";\n");
|
|
try indent(file);
|
|
try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
|
|
}
|
|
return local_name;
|
|
}
|
|
|
|
fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
|
|
try indent(file);
|
|
try file.main.writer().writeAll("zig_breakpoint();\n");
|
|
return null;
|
|
}
|
|
|
|
fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {
|
|
try indent(file);
|
|
try file.main.writer().writeAll("zig_unreachable();\n");
|
|
return null;
|
|
}
|
|
|
|
fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
|
|
try indent(file);
|
|
const writer = file.main.writer();
|
|
for (as.inputs) |i, index| {
|
|
if (i[0] == '{' and i[i.len - 1] == '}') {
|
|
const reg = i[1 .. i.len - 1];
|
|
const arg = as.args[index];
|
|
try writer.writeAll("register ");
|
|
try renderType(ctx, writer, arg.ty);
|
|
try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
|
|
// TODO merge constant handling into inst_map as well
|
|
if (arg.castTag(.constant)) |c| {
|
|
try renderValue(ctx, writer, arg.ty, c.val);
|
|
try writer.writeAll(";\n ");
|
|
} else {
|
|
const gop = try ctx.inst_map.getOrPut(arg);
|
|
if (!gop.found_existing) {
|
|
return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
|
|
}
|
|
try writer.print("{s};\n ", .{gop.entry.value});
|
|
}
|
|
} else {
|
|
return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
|
|
}
|
|
}
|
|
try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
|
|
if (as.output) |o| {
|
|
return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{});
|
|
}
|
|
if (as.inputs.len > 0) {
|
|
if (as.output == null) {
|
|
try writer.writeAll(" :");
|
|
}
|
|
try writer.writeAll(": ");
|
|
for (as.inputs) |i, index| {
|
|
if (i[0] == '{' and i[i.len - 1] == '}') {
|
|
const reg = i[1 .. i.len - 1];
|
|
const arg = as.args[index];
|
|
if (index > 0) {
|
|
try writer.writeAll(", ");
|
|
}
|
|
try writer.print("\"\"({s}_constant)", .{reg});
|
|
} else {
|
|
// This is blocked by the earlier test
|
|
unreachable;
|
|
}
|
|
}
|
|
}
|
|
try writer.writeAll(");\n");
|
|
return null;
|
|
}
|