stage2 llvm backend: implement assembly and ptrtoint
These AIR instructions are the next blockers for `zig test` to work for this backend. After this commit, the "hello world" x86_64 test case passes for the LLVM backend as well.
This commit is contained in:
@@ -9,6 +9,7 @@ const math = std.math;
|
||||
|
||||
const Module = @import("../Module.zig");
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
const Zir = @import("../Zir.zig");
|
||||
const Air = @import("../Air.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
|
||||
@@ -488,9 +489,9 @@ pub const DeclGen = struct {
|
||||
llvm_param[i] = try self.getLLVMType(fn_param);
|
||||
}
|
||||
|
||||
const fn_type = llvm.Type.functionType(
|
||||
const fn_type = llvm.functionType(
|
||||
try self.getLLVMType(return_type),
|
||||
if (fn_param_len == 0) null else llvm_param.ptr,
|
||||
llvm_param.ptr,
|
||||
@intCast(c_uint, fn_param_len),
|
||||
.False,
|
||||
);
|
||||
@@ -750,7 +751,7 @@ pub const FuncGen = struct {
|
||||
fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
|
||||
const air_tags = self.air.instructions.items(.tag);
|
||||
for (body) |inst| {
|
||||
const opt_value = switch (air_tags[inst]) {
|
||||
const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
|
||||
.add => try self.airAdd(inst),
|
||||
.sub => try self.airSub(inst),
|
||||
|
||||
@@ -775,6 +776,7 @@ pub const FuncGen = struct {
|
||||
.call => try self.airCall(inst),
|
||||
.cond_br => try self.airCondBr(inst),
|
||||
.intcast => try self.airIntCast(inst),
|
||||
.ptrtoint => try self.airPtrToInt(inst),
|
||||
.load => try self.airLoad(inst),
|
||||
.loop => try self.airLoop(inst),
|
||||
.not => try self.airNot(inst),
|
||||
@@ -783,6 +785,7 @@ pub const FuncGen = struct {
|
||||
.unreach => self.airUnreach(inst),
|
||||
.optional_payload => try self.airOptionalPayload(inst, false),
|
||||
.optional_payload_ptr => try self.airOptionalPayload(inst, true),
|
||||
.assembly => try self.airAssembly(inst),
|
||||
.dbg_stmt => blk: {
|
||||
// TODO: implement debug info
|
||||
break :blk null;
|
||||
@@ -821,7 +824,7 @@ pub const FuncGen = struct {
|
||||
// Do we need that?
|
||||
const call = self.builder.buildCall(
|
||||
llvm_fn,
|
||||
if (args.len == 0) null else llvm_param_vals.ptr,
|
||||
llvm_param_vals.ptr,
|
||||
@intCast(c_uint, args.len),
|
||||
"",
|
||||
);
|
||||
@@ -986,6 +989,128 @@ pub const FuncGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
// Eventually, the Zig compiler needs to be reworked to have inline assembly go
|
||||
// through the same parsing code regardless of backend, and have LLVM-flavored
|
||||
// inline assembly be *output* from that assembler.
|
||||
// We don't have such an assembler implemented yet though. For now, this
|
||||
// implementation feeds the inline assembly code directly to LLVM, same
|
||||
// as stage1.
|
||||
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const zir = self.dg.decl.namespace.file_scope.zir;
|
||||
const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end + outputs.len ..][0..args_len]);
|
||||
if (outputs_len > 1) {
|
||||
return self.todo("implement llvm codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
|
||||
if (!is_volatile and self.liveness.isUnused(inst)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
|
||||
defer llvm_constraints.deinit(self.gpa);
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
const llvm_params_len = args.len + @boolToInt(output_constraint != null);
|
||||
const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
|
||||
const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
|
||||
|
||||
var llvm_param_i: usize = 0;
|
||||
var total_i: usize = 0;
|
||||
|
||||
if (output_constraint) |constraint| {
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendSliceAssumeCapacity(constraint);
|
||||
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
const arg_llvm_value = try self.resolveInst(arg);
|
||||
|
||||
llvm_param_values[llvm_param_i] = arg_llvm_value;
|
||||
llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
|
||||
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendSliceAssumeCapacity(constraint);
|
||||
|
||||
llvm_param_i += 1;
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
const clobbers = zir.extra[extra_i..][0..clobbers_len];
|
||||
for (clobbers) |clobber_index| {
|
||||
const clobber = zir.nullTerminatedString(clobber_index);
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendSliceAssumeCapacity("~{");
|
||||
llvm_constraints.appendSliceAssumeCapacity(clobber);
|
||||
llvm_constraints.appendSliceAssumeCapacity("}");
|
||||
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
const ret_ty = self.air.typeOfIndex(inst);
|
||||
const ret_llvm_ty = try self.dg.getLLVMType(ret_ty);
|
||||
const llvm_fn_ty = llvm.functionType(
|
||||
ret_llvm_ty,
|
||||
llvm_param_types.ptr,
|
||||
@intCast(c_uint, llvm_param_types.len),
|
||||
.False,
|
||||
);
|
||||
const asm_fn = llvm.getInlineAsm(
|
||||
llvm_fn_ty,
|
||||
asm_source.ptr,
|
||||
asm_source.len,
|
||||
llvm_constraints.items.ptr,
|
||||
llvm_constraints.items.len,
|
||||
llvm.Bool.fromBool(is_volatile),
|
||||
.False,
|
||||
.ATT,
|
||||
);
|
||||
return self.builder.buildCall(
|
||||
asm_fn,
|
||||
llvm_param_values.ptr,
|
||||
@intCast(c_uint, llvm_param_values.len),
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
@@ -1087,6 +1212,16 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildIntCast2(operand, try self.dg.getLLVMType(inst_ty), llvm.Bool.fromBool(signed), "");
|
||||
}
|
||||
|
||||
fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
const dest_llvm_ty = try self.dg.getLLVMType(self.air.typeOfIndex(inst));
|
||||
return self.builder.buildPtrToInt(operand, dest_llvm_ty, "");
|
||||
}
|
||||
|
||||
fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
@@ -1168,7 +1303,7 @@ pub const FuncGen = struct {
|
||||
fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
_ = inst;
|
||||
const llvn_fn = self.getIntrinsic("llvm.debugtrap");
|
||||
_ = self.builder.buildCall(llvn_fn, null, 0, "");
|
||||
_ = self.builder.buildCall(llvn_fn, undefined, 0, "");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,12 @@ pub const Value = opaque {
|
||||
|
||||
pub const getNextInstruction = LLVMGetNextInstruction;
|
||||
extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value;
|
||||
|
||||
pub const typeOf = LLVMTypeOf;
|
||||
extern fn LLVMTypeOf(Val: *const Value) *const Type;
|
||||
};
|
||||
|
||||
pub const Type = opaque {
|
||||
pub const functionType = LLVMFunctionType;
|
||||
extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type;
|
||||
|
||||
pub const constNull = LLVMConstNull;
|
||||
extern fn LLVMConstNull(Ty: *const Type) *const Value;
|
||||
|
||||
@@ -152,6 +152,28 @@ extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value;
|
||||
pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
|
||||
extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
|
||||
|
||||
pub const getInlineAsm = LLVMGetInlineAsm;
|
||||
extern fn LLVMGetInlineAsm(
|
||||
Ty: *const Type,
|
||||
AsmString: [*]const u8,
|
||||
AsmStringSize: usize,
|
||||
Constraints: [*]const u8,
|
||||
ConstraintsSize: usize,
|
||||
HasSideEffects: Bool,
|
||||
IsAlignStack: Bool,
|
||||
Dialect: InlineAsmDialect,
|
||||
) *const Value;
|
||||
|
||||
pub const functionType = LLVMFunctionType;
|
||||
extern fn LLVMFunctionType(
|
||||
ReturnType: *const Type,
|
||||
ParamTypes: [*]*const Type,
|
||||
ParamCount: c_uint,
|
||||
IsVarArg: Bool,
|
||||
) *const Type;
|
||||
|
||||
pub const InlineAsmDialect = enum(c_uint) { ATT, Intel };
|
||||
|
||||
pub const Attribute = opaque {};
|
||||
|
||||
pub const Builder = opaque {
|
||||
@@ -159,7 +181,11 @@ pub const Builder = opaque {
|
||||
extern fn LLVMDisposeBuilder(Builder: *const Builder) void;
|
||||
|
||||
pub const positionBuilder = LLVMPositionBuilder;
|
||||
extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void;
|
||||
extern fn LLVMPositionBuilder(
|
||||
Builder: *const Builder,
|
||||
Block: *const BasicBlock,
|
||||
Instr: *const Value,
|
||||
) void;
|
||||
|
||||
pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd;
|
||||
extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void;
|
||||
@@ -168,10 +194,23 @@ pub const Builder = opaque {
|
||||
extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock;
|
||||
|
||||
pub const buildCall = LLVMBuildCall;
|
||||
extern fn LLVMBuildCall(*const Builder, Fn: *const Value, Args: ?[*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value;
|
||||
extern fn LLVMBuildCall(
|
||||
*const Builder,
|
||||
Fn: *const Value,
|
||||
Args: [*]*const Value,
|
||||
NumArgs: c_uint,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildCall2 = LLVMBuildCall2;
|
||||
extern fn LLVMBuildCall2(*const Builder, *const Type, Fn: *const Value, Args: [*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value;
|
||||
extern fn LLVMBuildCall2(
|
||||
*const Builder,
|
||||
*const Type,
|
||||
Fn: *const Value,
|
||||
Args: [*]*const Value,
|
||||
NumArgs: c_uint,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildRetVoid = LLVMBuildRetVoid;
|
||||
extern fn LLVMBuildRetVoid(*const Builder) *const Value;
|
||||
@@ -229,6 +268,9 @@ pub const Builder = opaque {
|
||||
|
||||
pub const buildExtractValue = LLVMBuildExtractValue;
|
||||
extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildPtrToInt = LLVMBuildPtrToInt;
|
||||
extern fn LLVMBuildPtrToInt(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
|
||||
};
|
||||
|
||||
pub const IntPredicate = enum(c_int) {
|
||||
@@ -534,12 +576,6 @@ pub const ObjectFormatType = enum(c_int) {
|
||||
XCOFF,
|
||||
};
|
||||
|
||||
pub const GetHostCPUName = LLVMGetHostCPUName;
|
||||
extern fn LLVMGetHostCPUName() ?[*:0]u8;
|
||||
|
||||
pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
|
||||
extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
|
||||
|
||||
pub const WriteArchive = ZigLLVMWriteArchive;
|
||||
extern fn ZigLLVMWriteArchive(
|
||||
archive_name: [*:0]const u8,
|
||||
|
||||
Reference in New Issue
Block a user