nativity/bootstrap/backend/c_transpiler.zig
2023-11-30 17:33:45 -06:00

1357 lines
68 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const equal = std.mem.eql;
const Compilation = @import("../Compilation.zig");
const logln = Compilation.logln;
const Module = Compilation.Module;
const data_structures = @import("../data_structures.zig");
const ArrayList = data_structures.ArrayList;
const AutoArrayHashMap = data_structures.AutoArrayHashMap;
const StringArrayHashMap = data_structures.StringArrayHashMap;
pub const Logger = enum {
g,
pub var bitset = std.EnumSet(Logger).initMany(&.{
.g,
});
};
const margin_width = 4;
pub const TranslationUnit = struct {
string_literals: ArrayList(u8) = .{},
primitive_type_declarations: ArrayList(u8) = .{},
type_forward_declarations: ArrayList(u8) = .{},
type_declarations: ArrayList(u8) = .{},
function_declarations: ArrayList(u8) = .{},
global_variable_declarations: ArrayList(u8) = .{},
function_definitions: ArrayList(u8) = .{},
syscall_bitset: SyscallBitset = SyscallBitset.initEmpty(),
struct_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{},
optional_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{},
function_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{},
slice_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{},
array_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{},
declaration_set: AutoArrayHashMap(Compilation.Declaration.Index, []const u8) = .{},
const SyscallBitset = std.StaticBitSet(6);
fn create(module: *Module, allocator: Allocator) !*TranslationUnit {
var unit = try allocator.create(TranslationUnit);
unit.* = .{};
try unit.primitive_type_declarations.appendSlice(allocator,
\\typedef unsigned char u8;
\\typedef unsigned short u16;
\\typedef unsigned int u32;
\\typedef unsigned long u64;
\\typedef u64 usize;
\\static_assert(sizeof(u8) == 1);
\\static_assert(sizeof(u16) == 2);
\\static_assert(sizeof(u32) == 4);
\\static_assert(sizeof(u64) == 8);
\\typedef signed char s8;
\\typedef signed short s16;
\\typedef signed int s32;
\\typedef signed long s64;
\\typedef s64 ssize;
\\static_assert(sizeof(s8) == 1);
\\static_assert(sizeof(s16) == 2);
\\static_assert(sizeof(s32) == 4);
\\static_assert(sizeof(s64) == 8);
\\
\\
);
{
var function_definitions = module.types.function_definitions.iterator();
while (function_definitions.nextIndex()) |function_definition_index| {
_ = try unit.writeFunctionDefinition(module, allocator, function_definition_index);
}
}
return unit;
}
fn writeFunctionDefinition(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_definition_index: Compilation.Function.Index) ![]const u8 {
const gop = try unit.function_set.getOrPut(allocator, function_definition_index);
if (!gop.found_existing) {
const function_definition = module.types.function_definitions.get(function_definition_index);
const function_prototype_type = function_definition.prototype;
const function_prototype = module.types.function_prototypes.get(module.types.array.get(function_prototype_type).function);
const function_name = try unit.writeFunctionHeader(module, &unit.function_declarations, allocator, function_definition_index);
gop.value_ptr.* = function_name;
_ = try unit.writeFunctionHeader(module, &unit.function_definitions, allocator, function_definition_index);
try unit.function_declarations.appendSlice(allocator, ";\n\n");
try unit.function_definitions.append(allocator, ' ');
try unit.writeBlock(module, &unit.function_definitions, allocator, function_definition.body, function_prototype.return_type, 0);
try unit.function_definitions.append(allocator, '\n');
}
return gop.value_ptr.*;
}
fn writeDeclaration(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, declaration_index: Compilation.Declaration.Index, indentation: usize) !void {
const declaration = module.values.declarations.get(declaration_index);
const mangle = false;
const name = try unit.renderDeclarationName(module, allocator, declaration_index, mangle);
if (declaration.mutability == .@"const") {
switch (module.types.array.get(declaration.type).*) {
.optional => |optional| switch (module.types.array.get(optional.element_type).*) {
.pointer => {},
else => try list.appendSlice(allocator, "const "),
},
.pointer => {},
.integer,
.@"struct",
.slice,
.bool,
=> try list.appendSlice(allocator, "const "),
else => |t| @panic(@tagName(t)),
//else => try list.appendSlice(allocator, "const "),
}
}
try unit.writeType(module, list, allocator, declaration.type);
try list.append(allocator, ' ');
try list.appendSlice(allocator, name);
try list.appendSlice(allocator, " = ");
try unit.writeValue(module, list, allocator, Compilation.Type.Index.invalid, indentation, .{
.value_index = declaration.init_value,
.type_index = declaration.type,
});
}
fn writeAssignment(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assignment_index: Compilation.Assignment.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void {
const assignment = module.values.assignments.get(assignment_index);
const left_type = module.values.array.get(assignment.source).getType(module);
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = assignment.destination,
.type_index = left_type,
});
try list.append(allocator, ' ');
switch (assignment.operation) {
.none => {},
.add => try list.append(allocator, '+'),
}
try list.appendSlice(allocator, "= ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = assignment.source,
.type_index = Compilation.Type.Index.invalid,
});
}
fn writeBlock(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, block_index: Compilation.Block.Index, function_return_type: Compilation.Type.Index, old_indentation: usize) !void {
try list.appendSlice(allocator, "{\n");
const block = module.values.blocks.get(block_index);
const indentation = old_indentation + 1;
for (block.statements.items) |statement_index| {
try list.appendNTimes(allocator, ' ', indentation * margin_width);
const statement = module.values.array.get(statement_index);
switch (statement.*) {
.declaration => |declaration_index| {
try unit.writeDeclaration(module, list, allocator, declaration_index, indentation);
try list.append(allocator, ';');
},
.assign => |assignment_index| {
try unit.writeAssignment(module, list, allocator, assignment_index, function_return_type, indentation);
try list.append(allocator, ';');
},
.@"return" => |return_index| {
const return_expr = module.values.returns.get(return_index);
try list.appendSlice(allocator, "return ");
const return_value = module.values.array.get(return_expr.value);
const return_value_type_index = return_value.getType(module);
// _ = return_value_type_index;
switch (module.types.array.get(function_return_type).*) {
.optional => switch (module.types.array.get(return_value_type_index).*) {
.optional => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = return_expr.value,
.type_index = function_return_type,
}),
else => {
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, function_return_type);
try list.appendSlice(allocator, ") {\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.appendSlice(allocator, ".value = ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = return_expr.value,
.type_index = return_value_type_index,
});
try list.appendSlice(allocator, ",\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.appendSlice(allocator, ".is_null = false,\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, '}');
},
},
else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = return_expr.value,
.type_index = function_return_type,
}),
}
try list.append(allocator, ';');
},
.syscall => |syscall_index| {
try unit.writeSyscall(module, list, allocator, syscall_index, function_return_type, indentation);
try list.append(allocator, ';');
},
.@"unreachable" => {
try writeUnreachable(list, allocator);
try list.append(allocator, ';');
},
.call => |call_index| {
try unit.writeCall(module, list, allocator, call_index, function_return_type, indentation);
try list.append(allocator, ';');
},
.branch => |branch_index| {
const branch = module.values.branches.get(branch_index);
try list.appendSlice(allocator, "if (");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = branch.expression,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ") ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = branch.taken_expression,
.type_index = function_return_type,
});
if (!branch.not_taken_expression.invalid) {
if (module.values.array.get(branch.taken_expression).* == .block) {
_ = list.pop();
try list.appendSlice(allocator, " else ");
} else {
unreachable;
}
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = branch.not_taken_expression,
.type_index = function_return_type,
});
if (module.values.array.get(branch.not_taken_expression).* == .block) {
continue;
}
}
},
.assembly_block => |assembly_block_index| {
try unit.writeAssembly(module, list, allocator, assembly_block_index, indentation);
try list.append(allocator, ';');
},
.loop => |loop_index| {
const loop = module.values.loops.get(loop_index);
try list.appendSlice(allocator, "for (");
if (!loop.pre.invalid) {
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = loop.pre,
.type_index = Compilation.Type.Index.invalid,
});
}
try list.appendSlice(allocator, "; ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = loop.condition,
.type_index = Compilation.Type.boolean,
});
try list.appendSlice(allocator, "; ");
if (!loop.post.invalid) {
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = loop.post,
.type_index = Compilation.Type.Index.invalid,
});
}
try list.appendSlice(allocator, ") ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = loop.body,
.type_index = Compilation.Type.Index.invalid,
});
},
.block => |new_block_index| {
try unit.writeBlock(module, list, allocator, new_block_index, function_return_type, indentation);
},
else => |t| @panic(@tagName(t)),
}
try list.append(allocator, '\n');
}
try list.appendNTimes(allocator, ' ', old_indentation * margin_width);
try list.appendSlice(allocator, "}\n");
}
const FunctionHeaderType = enum {
pointer,
header,
};
fn renderTypeName(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 {
const declaration_index = module.map.types.get(type_index).?;
const mangle = true;
const result = try unit.renderDeclarationName(module, allocator, declaration_index, mangle);
return result;
}
fn renderFunctionName(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 {
const function_definition = module.types.function_definitions.get(function_index);
const function_prototype_type = module.types.array.get(function_definition.prototype);
const function_prototype_index = function_prototype_type.function;
const function_prototype = module.types.function_prototypes.get(function_prototype_index);
const mangle = !(function_prototype.attributes.@"export" or function_prototype.attributes.@"extern");
const function_declaration_index = module.map.functions.get(function_index).?;
const name = try unit.renderDeclarationName(module, allocator, function_declaration_index, mangle);
return name;
}
fn renderDeclarationName(unit: *TranslationUnit, module: *Module, allocator: Allocator, declaration_index: Compilation.Declaration.Index, mangle: bool) anyerror![]const u8 {
const gop = try unit.declaration_set.getOrPut(allocator, declaration_index);
if (!gop.found_existing) {
const declaration = module.values.declarations.get(declaration_index);
const base_declaration_name = module.getName(declaration.name).?;
var list = ArrayList(u8){};
try list.insertSlice(allocator, 0, base_declaration_name);
if (mangle) {
var scope_index = declaration.scope;
var iterations: usize = 0;
switch (declaration.scope_type) {
.global => {
while (!scope_index.invalid) {
const scope = module.values.scopes.get(scope_index);
if (module.map.types.get(scope.type)) |type_declaration| {
const scope_type_declaration = module.values.declarations.get(type_declaration);
const scope_type_declaration_name = module.getName(scope_type_declaration.name).?;
try list.insert(allocator, 0, '_');
try list.insertSlice(allocator, 0, scope_type_declaration_name);
scope_index = scope.parent;
} else {
break;
}
iterations += 1;
}
},
.local => {},
}
}
// TODO: enhance declaration name rendering with file scope name
// const scope = declaration.scope;
gop.value_ptr.* = list.items;
switch (declaration.scope_type) {
.global => switch (module.types.array.get(declaration.type).*) {
.function,
.type,
=> {},
.@"struct" => {
try unit.writeDeclaration(module, &unit.global_variable_declarations, allocator, declaration_index, 0);
try unit.global_variable_declarations.append(allocator, ';');
try unit.global_variable_declarations.appendNTimes(allocator, '\n', 2);
},
else => |t| @panic(@tagName(t)),
},
.local => {},
}
}
assert(@intFromPtr(gop.value_ptr.*.ptr) != 0xaaaa_aaaa_aaaa_aaaa);
logln(.c, .g, "Rendering name: {s}", .{gop.value_ptr.*});
return gop.value_ptr.*;
}
fn writeFunctionPrototype(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_prototype_index: Compilation.Function.Prototype.Index, name: []const u8) !void {
const function_prototype = module.types.function_prototypes.get(function_prototype_index);
switch (function_prototype.attributes.calling_convention) {
.system_v => {},
.naked => try list.appendSlice(allocator, "[[gnu::naked]] "),
}
try unit.writeType(module, list, allocator, function_prototype.return_type);
try list.append(allocator, ' ');
try list.appendSlice(allocator, name);
try list.append(allocator, '(');
if (function_prototype.arguments) |function_arguments| {
for (function_arguments) |argument_index| {
const arg_declaration = module.values.declarations.get(argument_index);
try unit.writeType(module, list, allocator, arg_declaration.type);
try list.append(allocator, ' ');
const arg_name = module.getName(arg_declaration.name).?;
try list.appendSlice(allocator, arg_name);
try list.appendSlice(allocator, ", ");
}
_ = list.pop();
_ = list.pop();
}
try list.appendSlice(allocator, ")");
}
fn writeFunctionHeader(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 {
const name = try unit.renderFunctionName(module, allocator, function_index);
const function_definition = module.types.function_definitions.get(function_index);
const function_prototype_type = module.types.array.get(function_definition.prototype);
const function_prototype_index = function_prototype_type.function;
try unit.writeFunctionPrototype(module, list, allocator, function_prototype_index, name);
return name;
}
fn writeType(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, type_index: Compilation.Type.Index) anyerror!void {
const sema_type = module.types.array.get(type_index);
switch (sema_type.*) {
.void => try list.appendSlice(allocator, "void"),
.noreturn => try list.appendSlice(allocator, "[[noreturn]] void"),
.bool => try list.appendSlice(allocator, "bool"),
.integer => |integer| {
try list.append(allocator, switch (integer.signedness) {
.signed => 's',
.unsigned => 'u',
});
try list.writer(allocator).print("{}", .{integer.bit_count});
},
.pointer => |pointer| {
switch (module.types.array.get(pointer.element_type).*) {
.function => {
@panic("This should be unreachable");
},
else => {
if (pointer.@"const") {
try list.appendSlice(allocator, "const ");
}
try unit.writeType(module, list, allocator, pointer.element_type);
try list.append(allocator, '*');
},
}
},
.@"struct" => {
const name = try unit.cacheStructType(module, allocator, type_index);
try list.appendSlice(allocator, name);
},
.optional => {
const name = try unit.cacheOptionalType(module, allocator, type_index);
try list.appendSlice(allocator, name);
},
.slice => {
const name = try unit.cacheSliceType(module, allocator, type_index);
try list.appendSlice(allocator, name);
},
.array => {
const name = try unit.cacheArrayType(module, allocator, type_index);
try list.appendSlice(allocator, name);
},
.any => @panic("Internal compiler error: 'any' made it to the backend"),
else => |t| @panic(@tagName(t)),
}
}
fn writeCDeclaration(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, name: []const u8, type_index: Compilation.Type.Index) !void {
const declaration_type = module.types.array.get(type_index);
switch (declaration_type.*) {
.pointer => |pointer| {
switch (module.types.array.get(pointer.element_type).*) {
.function => |function| return try unit.writeFunctionPrototype(module, list, allocator, function, try std.mem.concat(allocator, u8, &.{ "(*", name, ")" })),
else => |t| @panic(@tagName(t)),
}
},
else => {},
}
try unit.writeType(module, list, allocator, type_index);
try list.append(allocator, ' ');
try list.appendSlice(allocator, name);
}
fn writeAssembly(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assembly_block_index: Compilation.Assembly.Block.Index, indentation: usize) !void {
const assembly_block = module.values.assembly_blocks.get(assembly_block_index);
try list.appendSlice(allocator, "__asm__ __volatile__(\n");
for (assembly_block.instructions) |instruction_index| {
const generic_instruction = module.values.assembly_instructions.get(instruction_index);
try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width);
try list.append(allocator, '"');
switch (module.descriptor.target.cpu.arch) {
.x86_64 => {
const architecture = @field(Compilation.Assembly, "x86_64");
const instruction: architecture.Instruction = @enumFromInt(generic_instruction.id);
const instruction_name = switch (instruction) {
.@"and" => "andq",
.xor => @tagName(instruction),
.call => "callq",
};
try list.appendSlice(allocator, instruction_name);
assert(generic_instruction.operands.len <= 2);
if (generic_instruction.operands.len > 0) {
try list.append(allocator, ' ');
var operand_i: usize = generic_instruction.operands.len;
while (operand_i > 0) {
operand_i -= 1;
const operand = generic_instruction.operands[operand_i];
switch (operand) {
.register => |generic_register| {
const register: architecture.Register = @enumFromInt(generic_register);
try list.append(allocator, '%');
try list.appendSlice(allocator, @tagName(register));
},
.number_literal => |number_literal| {
try list.writer(allocator).print("$0x{x}", .{number_literal});
},
.value_index => |value_index| {
try unit.writeValue(module, list, allocator, Compilation.Type.Index.invalid, indentation + 1, .{
.value_index = value_index,
.type_index = Compilation.Type.Index.invalid,
});
},
}
try list.appendSlice(allocator, ", ");
}
_ = list.pop();
_ = list.pop();
}
},
else => unreachable,
}
try list.appendSlice(allocator, "\\n\\t\"\n");
}
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, ')');
}
fn cacheStructType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 {
const t = module.types.array.get(type_index);
assert(t.* == .@"struct");
const gop = try unit.struct_type_set.getOrPut(allocator, type_index);
if (!gop.found_existing) {
const struct_type = module.types.structs.get(t.@"struct");
const type_name = try unit.renderTypeName(module, allocator, type_index);
gop.value_ptr.* = type_name;
// Forward declare the struct
{
try unit.type_forward_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_forward_declarations.appendSlice(allocator, type_name);
try unit.type_forward_declarations.append(allocator, ' ');
try unit.type_forward_declarations.appendSlice(allocator, type_name);
try unit.type_forward_declarations.appendSlice(allocator, ";\n");
}
// Actually declare the struct
{
try unit.type_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_declarations.appendSlice(allocator, type_name);
try unit.type_declarations.appendSlice(allocator, " {\n");
for (struct_type.fields.items) |struct_field_index| {
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
const struct_field = module.types.container_fields.get(struct_field_index);
const struct_field_name = module.getName(struct_field.name).?;
switch (struct_type.backing_type.invalid) {
false => {
try unit.writeType(module, &unit.type_declarations, allocator, struct_type.backing_type);
try unit.type_declarations.append(allocator, ' ');
try unit.type_declarations.appendSlice(allocator, struct_field_name);
try unit.type_declarations.appendSlice(allocator, " : ");
try unit.type_declarations.writer(allocator).print("{}", .{module.types.array.get(struct_field.type).getBitSize()});
},
true => try unit.writeCDeclaration(module, &unit.type_declarations, allocator, struct_field_name, struct_field.type),
}
try unit.type_declarations.appendSlice(allocator, ";\n");
}
try unit.type_declarations.appendSlice(allocator, "} ");
try unit.type_declarations.appendSlice(allocator, type_name);
try unit.type_declarations.appendSlice(allocator, ";\n\n");
}
}
assert(@intFromPtr(gop.value_ptr.*.ptr) != 0xaaaa_aaaa_aaaa_aaaa);
return gop.value_ptr.*;
}
fn cacheOptionalType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 {
const optional_type = module.types.array.get(type_index);
assert(optional_type.* == .optional);
const optional = optional_type.optional;
const gop = try unit.optional_type_set.getOrPut(allocator, optional.element_type);
if (!gop.found_existing) {
var type_name = ArrayList(u8){};
const optional_element_type = module.types.array.get(optional.element_type);
switch (optional_element_type.*) {
.pointer => {
try unit.writeType(module, &type_name, allocator, optional.element_type);
},
else => {
try type_name.appendSlice(allocator, "Optional_");
try unit.writeType(module, &type_name, allocator, optional.element_type);
if (!gop.found_existing) {
try unit.type_forward_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.append(allocator, ' ');
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.appendSlice(allocator, ";\n");
try unit.type_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, " {\n");
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
try unit.writeCDeclaration(module, &unit.type_declarations, allocator, "value", optional.element_type);
try unit.type_declarations.appendSlice(allocator, ";\n");
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
try unit.writeCDeclaration(module, &unit.type_declarations, allocator, "is_null", Compilation.Type.boolean);
try unit.type_declarations.appendSlice(allocator, ";\n");
try unit.type_declarations.appendSlice(allocator, "} ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, ";\n\n");
}
},
}
gop.value_ptr.* = type_name.items;
}
return gop.value_ptr.*;
}
fn cacheSliceType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 {
const slice = module.types.array.get(type_index).slice;
const gop = try unit.slice_type_set.getOrPut(allocator, slice.element_type);
if (!gop.found_existing) {
var type_name = ArrayList(u8){};
try type_name.appendSlice(allocator, "Slice_");
try unit.writeType(module, &type_name, allocator, slice.element_type);
gop.value_ptr.* = type_name.items;
try unit.type_forward_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.append(allocator, ' ');
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.appendSlice(allocator, ";\n");
try unit.type_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, " {\n");
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
try unit.writeType(module, &unit.type_declarations, allocator, slice.element_type);
try unit.type_declarations.appendSlice(allocator, "* ptr;\n");
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
try unit.type_declarations.appendSlice(allocator, "usize len;\n");
try unit.type_declarations.appendSlice(allocator, "} ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, ";\n\n");
}
return gop.value_ptr.*;
}
fn cacheArrayType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 {
const array = module.types.array.get(type_index).array;
const gop = try unit.array_type_set.getOrPut(allocator, array.element_type);
if (!gop.found_existing) {
var type_name = ArrayList(u8){};
try type_name.appendSlice(allocator, "Array_");
try unit.writeType(module, &type_name, allocator, array.element_type);
try type_name.writer(allocator).print("_{}", .{array.element_count});
gop.value_ptr.* = type_name.items;
try unit.type_forward_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.append(allocator, ' ');
try unit.type_forward_declarations.appendSlice(allocator, type_name.items);
try unit.type_forward_declarations.appendSlice(allocator, ";\n");
try unit.type_declarations.appendSlice(allocator, "typedef struct ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, " {\n");
try unit.type_declarations.appendNTimes(allocator, ' ', margin_width);
try unit.writeType(module, &unit.type_declarations, allocator, array.element_type);
try unit.type_declarations.appendSlice(allocator, " value\n");
try unit.type_declarations.writer(allocator).print("[{}];\n", .{array.element_count});
try unit.type_declarations.appendSlice(allocator, "} ");
try unit.type_declarations.appendSlice(allocator, type_name.items);
try unit.type_declarations.appendSlice(allocator, ";\n\n");
}
return gop.value_ptr.*;
}
fn writeSyscall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, syscall_index: Compilation.Syscall.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void {
const syscall = module.values.syscalls.get(syscall_index);
const arguments = syscall.getArguments();
if (!unit.syscall_bitset.isSet(arguments.len - 1)) {
try unit.function_declarations.appendSlice(allocator, "static __inline u64 syscall");
try unit.function_declarations.writer(allocator).print("{}(", .{arguments.len});
try unit.function_declarations.appendSlice(allocator, "u64 n, ");
for (0..arguments.len) |arg_i| {
try unit.function_declarations.writer(allocator).print("u64 arg{}, ", .{arg_i});
}
_ = unit.function_declarations.pop();
_ = unit.function_declarations.pop();
try unit.function_declarations.appendSlice(allocator,
\\) {
\\
);
const simple_register_argument_count = @min(arguments.len, 3);
const complex_register_argument_count = arguments.len - simple_register_argument_count;
const simple_argument_registers = [_]u8{ 'D', 'S', 'd' };
const complex_argument_registers = [_]u8{ 10, 8, 9 };
for (0..complex_register_argument_count) |i| {
try unit.function_declarations.appendNTimes(allocator, ' ', indentation * margin_width);
try unit.function_declarations.writer(allocator).print("register unsigned long r{} __asm__(\"r{}\") = arg{};\n", .{ complex_argument_registers[i], complex_argument_registers[i], 3 + i });
}
try unit.function_declarations.appendSlice(allocator,
\\ unsigned long ret;
\\
\\ __asm__ __volatile__("syscall"
\\ : "=a"(ret)
\\ : "a"(n),
);
for (0..simple_register_argument_count, simple_argument_registers[0..simple_register_argument_count]) |arg_i, arg_register| {
try unit.function_declarations.writer(allocator).print("\"{c}\"(arg{}), ", .{ arg_register, arg_i });
}
for (complex_argument_registers[0..complex_register_argument_count]) |arg_register| {
try unit.function_declarations.writer(allocator).print("\"r\"(r{}), ", .{arg_register});
}
_ = unit.function_declarations.pop();
_ = unit.function_declarations.pop();
try unit.function_declarations.appendSlice(allocator,
\\
\\ : "rcx", "r11", "memory"
\\ );
\\
\\ return ret;
\\}
\\
\\
);
unit.syscall_bitset.set(arguments.len - 1);
}
try list.writer(allocator).print("syscall{}(", .{arguments.len});
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = syscall.number,
.type_index = function_return_type,
});
try list.appendSlice(allocator, ", ");
for (arguments) |argument_index| {
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = argument_index,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ", ");
}
_ = list.pop();
_ = list.pop();
try list.append(allocator, ')');
}
fn writeUnreachable(list: *ArrayList(u8), allocator: Allocator) !void {
try list.appendSlice(allocator, "__builtin_unreachable()");
}
fn writeCall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, call_index: Compilation.Call.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void {
const call = module.values.calls.get(call_index);
const call_value = module.values.array.get(call.value);
switch (call_value.*) {
.function_definition => |function_definition_index| {
const name = try unit.renderFunctionName(module, allocator, function_definition_index);
try list.appendSlice(allocator, name);
try list.append(allocator, '(');
},
.field_access => |field_access_index| {
const field_access = module.values.field_accesses.get(field_access_index);
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = field_access.declaration_reference,
.type_index = function_return_type,
});
const left_type = module.types.array.get(module.values.array.get(field_access.declaration_reference).declaration_reference.type);
const is_pointer = switch (left_type.*) {
.pointer => true,
else => false,
};
if (is_pointer) {
try list.appendSlice(allocator, "->");
} else {
try list.append(allocator, '.');
}
const field = module.types.container_fields.get(field_access.field);
const field_name = module.getName(field.name).?;
try list.appendSlice(allocator, field_name);
try list.append(allocator, '(');
},
else => |t| @panic(@tagName(t)),
}
if (!call.arguments.invalid) {
const argument_list = module.values.argument_lists.get(call.arguments);
if (argument_list.array.items.len > 0) {
for (argument_list.array.items) |argument_index| {
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = argument_index,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ", ");
}
_ = list.pop();
_ = list.pop();
}
}
try list.append(allocator, ')');
}
const ValueArguments = struct {
value_index: Compilation.Value.Index,
type_index: Compilation.Type.Index,
};
fn writeValue(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_return_type: Compilation.Type.Index, indentation: usize, arguments: ValueArguments) anyerror!void {
const value_index = arguments.value_index;
const type_index = arguments.type_index;
_ = type_index;
const value = module.values.array.get(value_index);
switch (value.*) {
.declaration => |declaration_index| {
try unit.writeDeclaration(module, list, allocator, declaration_index, indentation);
},
.assign => |assignment_index| {
try unit.writeAssignment(module, list, allocator, assignment_index, function_return_type, indentation);
},
.integer => |integer| {
try list.writer(allocator).print("{}", .{integer.value});
},
.declaration_reference => |declaration_reference| {
const mangle = true;
const name = try unit.renderDeclarationName(module, allocator, declaration_reference.value, mangle);
try list.appendSlice(allocator, name);
},
.binary_operation => |binary_operation_index| {
const binary_operation = module.values.binary_operations.get(binary_operation_index);
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = binary_operation.left,
.type_index = binary_operation.type,
});
try list.append(allocator, ' ');
switch (binary_operation.id) {
.add => try list.append(allocator, '+'),
.sub => try list.append(allocator, '-'),
.bit_and => try list.append(allocator, '&'),
.bit_or => try list.append(allocator, '|'),
.bit_xor => try list.append(allocator, '^'),
.multiply => try list.append(allocator, '*'),
.divide => try list.append(allocator, '/'),
.compare_greater_than => try list.append(allocator, '>'),
.compare_less_than => try list.append(allocator, '<'),
.shift_left => try list.appendSlice(allocator, "<<"),
.shift_right => try list.appendSlice(allocator, ">>"),
.compare_equal => try list.appendSlice(allocator, "=="),
.compare_greater_or_equal => try list.appendSlice(allocator, ">="),
.compare_less_or_equal => try list.appendSlice(allocator, "<="),
}
try list.append(allocator, ' ');
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = binary_operation.right,
.type_index = binary_operation.type,
});
},
.sign_extend => |cast_index| {
const sign_extend = module.values.casts.get(cast_index);
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = sign_extend.value,
.type_index = sign_extend.type,
});
},
.cast => |cast_index| {
const cast = module.values.casts.get(cast_index);
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, cast.type);
try list.append(allocator, ')');
const cast_value = module.values.array.get(cast.value);
const cast_value_type = module.types.array.get(cast_value.getType(module));
switch (cast_value_type.*) {
.@"struct" => |struct_index| {
const struct_type = module.types.structs.get(struct_index);
switch (struct_type.backing_type.invalid) {
false => {
try list.appendSlice(allocator, "*(");
try unit.writeType(module, list, allocator, struct_type.backing_type);
try list.appendSlice(allocator, "*)&(");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = cast.value,
.type_index = function_return_type,
});
try list.append(allocator, ')');
},
true => @panic("Unable to bitcast non-packed struct"),
}
},
else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = cast.value,
.type_index = Compilation.Type.Index.invalid,
}),
}
},
.string_literal => |string_literal_descriptor| {
try list.appendSlice(allocator, "(const u8*)");
const string_literal = module.getName(string_literal_descriptor.hash) orelse unreachable;
try list.append(allocator, '"');
try list.appendSlice(allocator, string_literal);
try list.append(allocator, '"');
},
.@"unreachable" => {
try writeUnreachable(list, allocator);
},
.call => |call_index| try unit.writeCall(module, list, allocator, call_index, function_return_type, indentation),
.syscall => |syscall_index| try unit.writeSyscall(module, list, allocator, syscall_index, function_return_type, indentation),
.bool => |boolean| try list.appendSlice(allocator, if (boolean) "true" else "false"),
.block => |block_index| try unit.writeBlock(module, list, allocator, block_index, function_return_type, indentation),
.unary_operation => |unary_operation_index| {
const unary_operation = module.values.unary_operations.get(unary_operation_index);
const expression_character: u8 = switch (unary_operation.id) {
.boolean_not => '!',
.negation => '-',
.address_of => '&',
.pointer_dereference => '*',
};
try list.append(allocator, expression_character);
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = unary_operation.value,
.type_index = unary_operation.type,
});
try list.append(allocator, ')');
},
.container_initialization => |container_initialization_index| {
const container_initialization = module.values.container_initializations.get(container_initialization_index);
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, container_initialization.type);
try list.appendSlice(allocator, ") {\n");
const container_type = module.types.array.get(container_initialization.type);
const container_fields = switch (container_type.*) {
.@"struct" => module.types.structs.get(container_type.@"struct").fields,
else => |t| @panic(@tagName(t)),
};
for (container_initialization.field_initializations.items, container_fields.items) |field_initialization_index, container_field_index| {
try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width);
try list.append(allocator, '.');
const container_field = module.types.container_fields.get(container_field_index);
const field_name = module.getName(container_field.name).?;
try list.appendSlice(allocator, field_name);
try list.appendSlice(allocator, " = ");
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = field_initialization_index,
.type_index = container_field.type,
});
try list.appendSlice(allocator, ",\n");
}
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, '}');
},
.field_access => |field_access_index| {
const field_access = module.values.field_accesses.get(field_access_index);
const left = module.values.array.get(field_access.declaration_reference);
const left_type = module.types.array.get(left.getType(module));
const right_field = module.types.container_fields.get(field_access.field);
const right_field_name = module.getName(right_field.name).?;
const is_pointer = switch (left_type.*) {
.@"struct" => false,
.pointer => true,
else => |t| @panic(@tagName(t)),
};
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = field_access.declaration_reference,
.type_index = right_field.type,
});
if (is_pointer) {
try list.appendSlice(allocator, "->");
} else {
try list.append(allocator, '.');
}
try list.appendSlice(allocator, right_field_name);
},
.pointer_null_literal => try list.appendSlice(allocator, "nullptr"),
.optional_null_literal => {
assert(!arguments.type_index.invalid);
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, arguments.type_index);
try list.appendSlice(allocator, ") { .is_null = true }");
},
.slice => |slice_index| {
const slice = module.values.slices.get(slice_index);
const sliceable = module.values.array.get(slice.sliceable);
const sliceable_type_index = switch (sliceable.*) {
.declaration_reference => |declaration_reference| declaration_reference.type,
else => |t| @panic(@tagName(t)),
};
const sliceable_type = module.types.array.get(sliceable_type_index);
const sliceable_element_type = switch (sliceable_type.*) {
.pointer => |pointer| pointer.element_type,
.slice => |slice_type| slice_type.element_type,
else => |t| @panic(@tagName(t)),
};
try list.appendSlice(allocator, "(Slice_");
try unit.writeType(module, list, allocator, sliceable_element_type);
try list.appendSlice(allocator, ") {\n");
try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width);
try list.appendSlice(allocator, ".ptr = ");
switch (sliceable_type.*) {
.pointer => {
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.sliceable,
.type_index = sliceable_type_index,
});
try list.appendSlice(allocator, ") + (");
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.start,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, "),\n");
},
.slice => {
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.sliceable,
.type_index = sliceable_type_index,
});
try list.appendSlice(allocator, ").ptr + (");
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.start,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, "),\n");
},
else => |t| @panic(@tagName(t)),
}
try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width);
try list.appendSlice(allocator, ".len = ");
switch (sliceable_type.*) {
.pointer => {
switch (slice.range.end.invalid) {
false => {
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.end,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ") - (");
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.start,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ")\n");
},
true => {
unreachable;
},
}
},
.slice => {
switch (slice.range.end.invalid) {
false => {
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.end,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ") - (");
try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{
.value_index = slice.range.start,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ")\n");
},
true => {
unreachable;
},
}
},
else => |t| @panic(@tagName(t)),
}
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, '}');
},
.function_definition => |function_definition_index| {
const function_name = try unit.writeFunctionDefinition(module, allocator, function_definition_index);
try list.appendSlice(allocator, function_name);
},
.optional_check => |optional_check_index| {
const optional_check = module.values.optional_checks.get(optional_check_index);
const optional_type = module.types.array.get(module.values.array.get(optional_check.value).getType(module));
assert(optional_type.* == .optional);
const optional_element_type = module.types.array.get(optional_type.optional.element_type);
const is_null_suffix_expression = switch (optional_element_type.*) {
.pointer => false,
else => true,
};
if (is_null_suffix_expression) {
try list.append(allocator, '!');
}
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = optional_check.value,
.type_index = Compilation.Type.Index.invalid,
});
try list.append(allocator, ')');
if (is_null_suffix_expression) {
try list.appendSlice(allocator, ".is_null");
}
},
.optional_unwrap => |optional_unwrap_index| {
const optional_unwrap = module.values.optional_unwraps.get(optional_unwrap_index);
const optional_value = module.values.array.get(optional_unwrap.value);
const optional_type = module.types.array.get(optional_value.getType(module));
assert(optional_type.* == .optional);
const optional_element_type_index = optional_type.optional.element_type;
const optional_element_type = module.types.array.get(optional_element_type_index);
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = optional_unwrap.value,
.type_index = optional_element_type_index,
});
try list.append(allocator, ')');
switch (optional_element_type.*) {
.pointer => {},
else => try list.appendSlice(allocator, ".value"),
}
},
.slice_access => |slice_access_index| {
const slice_access = module.values.slice_accesses.get(slice_access_index);
try list.append(allocator, '(');
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = slice_access.value,
.type_index = slice_access.type,
});
try list.appendSlice(allocator, ").");
try list.appendSlice(allocator, @tagName(slice_access.field));
},
.indexed_access => |indexed_access_index| {
const indexed_access = module.values.indexed_accesses.get(indexed_access_index);
try list.append(allocator, '(');
const indexed_expression_index = indexed_access.indexed_expression;
const indexed_expression = module.values.array.get(indexed_expression_index);
const indexed_expression_type_index = indexed_expression.getType(module);
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = indexed_expression_index,
.type_index = indexed_expression_type_index,
});
const indexed_expression_type = module.types.array.get(indexed_expression_type_index);
switch (indexed_expression_type.*) {
.slice => {
try list.appendSlice(allocator, ".ptr");
},
else => |t| @panic(@tagName(t)),
}
try list.appendSlice(allocator, ")[");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = indexed_access.index_expression,
.type_index = Compilation.Type.Index.invalid,
});
try list.append(allocator, ']');
},
.optional_cast => |cast_index| {
const optional_cast = module.values.casts.get(cast_index);
const optional_type = module.types.array.get(optional_cast.type);
switch (optional_type.*) {
.optional => |optional| {
const optional_element_type = module.types.array.get(optional.element_type);
switch (optional_element_type.*) {
.pointer => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = optional_cast.value,
.type_index = optional.element_type,
}),
else => {
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, optional_cast.type);
try list.appendSlice(allocator, ") {\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.appendSlice(allocator, ".value = ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = optional_cast.value,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ",\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.appendSlice(allocator, ".is_null = false,\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, '}');
},
}
},
else => |t| @panic(@tagName(t)),
}
},
.undefined => try list.appendSlice(allocator, "{}"),
.array_coerce_to_slice => |cast_index| {
const array_coerce_to_slice = module.values.casts.get(cast_index);
try list.append(allocator, '(');
try unit.writeType(module, list, allocator, array_coerce_to_slice.type);
try list.appendSlice(allocator, ") {\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.appendSlice(allocator, ".ptr = ");
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{
.value_index = array_coerce_to_slice.value,
.type_index = Compilation.Type.Index.invalid,
});
try list.appendSlice(allocator, ".value,\n");
try list.appendNTimes(allocator, ' ', indentation * margin_width);
const array_value = module.values.array.get(array_coerce_to_slice.value);
const array_type = module.types.array.get(array_value.getType(module));
const array_length = switch (array_type.*) {
.array => |array| array.element_count,
else => |t| @panic(@tagName(t)),
};
try list.writer(allocator).print(".len = {},\n", .{array_length});
try list.appendNTimes(allocator, ' ', indentation * margin_width);
try list.append(allocator, '}');
},
else => |t| @panic(@tagName(t)),
}
}
};
pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compilation.Module.Descriptor) !void {
const allocator = compilation.base_allocator;
var unit = try TranslationUnit.create(module, allocator);
const c_source_file_path = try std.mem.concat(allocator, u8, &.{ descriptor.executable_path, ".c" });
const c_source_file = try std.fs.cwd().createFile(c_source_file_path, .{});
try unit.type_forward_declarations.append(allocator, '\n');
var offset: u64 = 0;
const slices = [_][]const u8{ unit.primitive_type_declarations.items, unit.type_forward_declarations.items, unit.type_declarations.items, unit.function_declarations.items, unit.global_variable_declarations.items, unit.string_literals.items, unit.function_definitions.items };
for (slices) |slice| {
try c_source_file.pwriteAll(slice, offset);
offset += slice.len;
}
c_source_file.close();
const c_source_file_realpath = try std.fs.cwd().realpathAlloc(allocator, c_source_file_path);
const c_flags = [_][]const u8{
"-std=c2x",
"-g",
};
var zig_command_line = ArrayList([]const u8){};
try zig_command_line.append(allocator, "zig");
try zig_command_line.append(allocator, "build-exe");
try zig_command_line.append(allocator, try std.mem.concat(allocator, u8, &.{ "-femit-bin=", descriptor.executable_path }));
try zig_command_line.append(allocator, "-cflags");
for (c_flags) |c_flag| {
try zig_command_line.append(allocator, c_flag);
}
try zig_command_line.append(allocator, "--");
try zig_command_line.append(allocator, c_source_file_realpath);
const run_result = try std.ChildProcess.run(.{
.allocator = allocator,
.argv = zig_command_line.items,
});
switch (run_result.term) {
.Exited => |exit_code| {
if (exit_code != 0) {
for (zig_command_line.items) |arg| {
std.debug.print("{s} ", .{arg});
}
std.debug.panic("\nZig command exited with code {}:\n{s}", .{ exit_code, run_result.stderr });
}
},
else => |t| @panic(@tagName(t)),
}
}