authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-24 23:54:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-24 23:54:20-07:00
logb87105c9217e41cb39c09d9a569ac8bc006c2eef
treef3b48b3e500123ed71962743556e96ff1444b1d0
parent653c851e6233a105cb151602517d0222b1128ea7

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.

2 files changed, 188 insertions(+), 17 deletions(-)

src/codegen/llvm.zig+140-5
......@@ -9,6 +9,7 @@ const math = std.math;
99
1010const Module = @import("../Module.zig");
1111const TypedValue = @import("../TypedValue.zig");
12const Zir = @import("../Zir.zig");
1213const Air = @import("../Air.zig");
1314const Liveness = @import("../Liveness.zig");
1415
......@@ -488,9 +489,9 @@ pub const DeclGen = struct {
488489 llvm_param[i] = try self.getLLVMType(fn_param);
489490 }
490491
491 const fn_type = llvm.Type.functionType(
492 const fn_type = llvm.functionType(
492493 try self.getLLVMType(return_type),
493 if (fn_param_len == 0) null else llvm_param.ptr,
494 llvm_param.ptr,
494495 @intCast(c_uint, fn_param_len),
495496 .False,
496497 );
......@@ -750,7 +751,7 @@ pub const FuncGen = struct {
750751 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
751752 const air_tags = self.air.instructions.items(.tag);
752753 for (body) |inst| {
753 const opt_value = switch (air_tags[inst]) {
754 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
754755 .add => try self.airAdd(inst),
755756 .sub => try self.airSub(inst),
756757
......@@ -775,6 +776,7 @@ pub const FuncGen = struct {
775776 .call => try self.airCall(inst),
776777 .cond_br => try self.airCondBr(inst),
777778 .intcast => try self.airIntCast(inst),
779 .ptrtoint => try self.airPtrToInt(inst),
778780 .load => try self.airLoad(inst),
779781 .loop => try self.airLoop(inst),
780782 .not => try self.airNot(inst),
......@@ -783,6 +785,7 @@ pub const FuncGen = struct {
783785 .unreach => self.airUnreach(inst),
784786 .optional_payload => try self.airOptionalPayload(inst, false),
785787 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
788 .assembly => try self.airAssembly(inst),
786789 .dbg_stmt => blk: {
787790 // TODO: implement debug info
788791 break :blk null;
......@@ -821,7 +824,7 @@ pub const FuncGen = struct {
821824 // Do we need that?
822825 const call = self.builder.buildCall(
823826 llvm_fn,
824 if (args.len == 0) null else llvm_param_vals.ptr,
827 llvm_param_vals.ptr,
825828 @intCast(c_uint, args.len),
826829 "",
827830 );
......@@ -986,6 +989,128 @@ pub const FuncGen = struct {
986989 return null;
987990 }
988991
992 fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
993 // Eventually, the Zig compiler needs to be reworked to have inline assembly go
994 // through the same parsing code regardless of backend, and have LLVM-flavored
995 // inline assembly be *output* from that assembler.
996 // We don't have such an assembler implemented yet though. For now, this
997 // implementation feeds the inline assembly code directly to LLVM, same
998 // as stage1.
999
1000 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1001 const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);
1002 const zir = self.dg.decl.namespace.file_scope.zir;
1003 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
1004 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1005 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1006 const outputs_len = @truncate(u5, extended.small);
1007 const args_len = @truncate(u5, extended.small >> 5);
1008 const clobbers_len = @truncate(u5, extended.small >> 10);
1009 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1010 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..outputs_len]);
1011 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end + outputs.len ..][0..args_len]);
1012 if (outputs_len > 1) {
1013 return self.todo("implement llvm codegen for asm with more than 1 output", .{});
1014 }
1015
1016 var extra_i: usize = zir_extra.end;
1017 const output_constraint: ?[]const u8 = out: {
1018 var i: usize = 0;
1019 while (i < outputs_len) : (i += 1) {
1020 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1021 extra_i = output.end;
1022 break :out zir.nullTerminatedString(output.data.constraint);
1023 }
1024 break :out null;
1025 };
1026
1027 if (!is_volatile and self.liveness.isUnused(inst)) {
1028 return null;
1029 }
1030
1031 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
1032 defer llvm_constraints.deinit(self.gpa);
1033
1034 var arena_allocator = std.heap.ArenaAllocator.init(self.gpa);
1035 defer arena_allocator.deinit();
1036 const arena = &arena_allocator.allocator;
1037
1038 const llvm_params_len = args.len + @boolToInt(output_constraint != null);
1039 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
1040 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
1041
1042 var llvm_param_i: usize = 0;
1043 var total_i: usize = 0;
1044
1045 if (output_constraint) |constraint| {
1046 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
1047 if (total_i != 0) {
1048 llvm_constraints.appendAssumeCapacity(',');
1049 }
1050 llvm_constraints.appendSliceAssumeCapacity(constraint);
1051
1052 total_i += 1;
1053 }
1054
1055 for (args) |arg| {
1056 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1057 extra_i = input.end;
1058 const constraint = zir.nullTerminatedString(input.data.constraint);
1059 const arg_llvm_value = try self.resolveInst(arg);
1060
1061 llvm_param_values[llvm_param_i] = arg_llvm_value;
1062 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
1063
1064 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
1065 if (total_i != 0) {
1066 llvm_constraints.appendAssumeCapacity(',');
1067 }
1068 llvm_constraints.appendSliceAssumeCapacity(constraint);
1069
1070 llvm_param_i += 1;
1071 total_i += 1;
1072 }
1073
1074 const clobbers = zir.extra[extra_i..][0..clobbers_len];
1075 for (clobbers) |clobber_index| {
1076 const clobber = zir.nullTerminatedString(clobber_index);
1077 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
1078 if (total_i != 0) {
1079 llvm_constraints.appendAssumeCapacity(',');
1080 }
1081 llvm_constraints.appendSliceAssumeCapacity("~{");
1082 llvm_constraints.appendSliceAssumeCapacity(clobber);
1083 llvm_constraints.appendSliceAssumeCapacity("}");
1084
1085 total_i += 1;
1086 }
1087
1088 const ret_ty = self.air.typeOfIndex(inst);
1089 const ret_llvm_ty = try self.dg.getLLVMType(ret_ty);
1090 const llvm_fn_ty = llvm.functionType(
1091 ret_llvm_ty,
1092 llvm_param_types.ptr,
1093 @intCast(c_uint, llvm_param_types.len),
1094 .False,
1095 );
1096 const asm_fn = llvm.getInlineAsm(
1097 llvm_fn_ty,
1098 asm_source.ptr,
1099 asm_source.len,
1100 llvm_constraints.items.ptr,
1101 llvm_constraints.items.len,
1102 llvm.Bool.fromBool(is_volatile),
1103 .False,
1104 .ATT,
1105 );
1106 return self.builder.buildCall(
1107 asm_fn,
1108 llvm_param_values.ptr,
1109 @intCast(c_uint, llvm_param_values.len),
1110 "",
1111 );
1112 }
1113
9891114 fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
9901115 if (self.liveness.isUnused(inst))
9911116 return null;
......@@ -1087,6 +1212,16 @@ pub const FuncGen = struct {
10871212 return self.builder.buildIntCast2(operand, try self.dg.getLLVMType(inst_ty), llvm.Bool.fromBool(signed), "");
10881213 }
10891214
1215 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1216 if (self.liveness.isUnused(inst))
1217 return null;
1218
1219 const un_op = self.air.instructions.items(.data)[inst].un_op;
1220 const operand = try self.resolveInst(un_op);
1221 const dest_llvm_ty = try self.dg.getLLVMType(self.air.typeOfIndex(inst));
1222 return self.builder.buildPtrToInt(operand, dest_llvm_ty, "");
1223 }
1224
10901225 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
10911226 if (self.liveness.isUnused(inst))
10921227 return null;
......@@ -1168,7 +1303,7 @@ pub const FuncGen = struct {
11681303 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
11691304 _ = inst;
11701305 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
1171 _ = self.builder.buildCall(llvn_fn, null, 0, "");
1306 _ = self.builder.buildCall(llvn_fn, undefined, 0, "");
11721307 return null;
11731308 }
11741309
src/codegen/llvm/bindings.zig+48-12
......@@ -68,12 +68,12 @@ pub const Value = opaque {
6868
6969 pub const getNextInstruction = LLVMGetNextInstruction;
7070 extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value;
71
72 pub const typeOf = LLVMTypeOf;
73 extern fn LLVMTypeOf(Val: *const Value) *const Type;
7174};
7275
7376pub const Type = opaque {
74 pub const functionType = LLVMFunctionType;
75 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type;
76
7777 pub const constNull = LLVMConstNull;
7878 extern fn LLVMConstNull(Ty: *const Type) *const Value;
7979
......@@ -152,6 +152,28 @@ extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value;
152152pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
153153extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
154154
155pub const getInlineAsm = LLVMGetInlineAsm;
156extern fn LLVMGetInlineAsm(
157 Ty: *const Type,
158 AsmString: [*]const u8,
159 AsmStringSize: usize,
160 Constraints: [*]const u8,
161 ConstraintsSize: usize,
162 HasSideEffects: Bool,
163 IsAlignStack: Bool,
164 Dialect: InlineAsmDialect,
165) *const Value;
166
167pub const functionType = LLVMFunctionType;
168extern fn LLVMFunctionType(
169 ReturnType: *const Type,
170 ParamTypes: [*]*const Type,
171 ParamCount: c_uint,
172 IsVarArg: Bool,
173) *const Type;
174
175pub const InlineAsmDialect = enum(c_uint) { ATT, Intel };
176
155177pub const Attribute = opaque {};
156178
157179pub const Builder = opaque {
......@@ -159,7 +181,11 @@ pub const Builder = opaque {
159181 extern fn LLVMDisposeBuilder(Builder: *const Builder) void;
160182
161183 pub const positionBuilder = LLVMPositionBuilder;
162 extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void;
184 extern fn LLVMPositionBuilder(
185 Builder: *const Builder,
186 Block: *const BasicBlock,
187 Instr: *const Value,
188 ) void;
163189
164190 pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd;
165191 extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void;
......@@ -168,10 +194,23 @@ pub const Builder = opaque {
168194 extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock;
169195
170196 pub const buildCall = LLVMBuildCall;
171 extern fn LLVMBuildCall(*const Builder, Fn: *const Value, Args: ?[*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value;
197 extern fn LLVMBuildCall(
198 *const Builder,
199 Fn: *const Value,
200 Args: [*]*const Value,
201 NumArgs: c_uint,
202 Name: [*:0]const u8,
203 ) *const Value;
172204
173205 pub const buildCall2 = LLVMBuildCall2;
174 extern fn LLVMBuildCall2(*const Builder, *const Type, Fn: *const Value, Args: [*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value;
206 extern fn LLVMBuildCall2(
207 *const Builder,
208 *const Type,
209 Fn: *const Value,
210 Args: [*]*const Value,
211 NumArgs: c_uint,
212 Name: [*:0]const u8,
213 ) *const Value;
175214
176215 pub const buildRetVoid = LLVMBuildRetVoid;
177216 extern fn LLVMBuildRetVoid(*const Builder) *const Value;
......@@ -229,6 +268,9 @@ pub const Builder = opaque {
229268
230269 pub const buildExtractValue = LLVMBuildExtractValue;
231270 extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value;
271
272 pub const buildPtrToInt = LLVMBuildPtrToInt;
273 extern fn LLVMBuildPtrToInt(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
232274};
233275
234276pub const IntPredicate = enum(c_int) {
......@@ -534,12 +576,6 @@ pub const ObjectFormatType = enum(c_int) {
534576 XCOFF,
535577};
536578
537pub const GetHostCPUName = LLVMGetHostCPUName;
538extern fn LLVMGetHostCPUName() ?[*:0]u8;
539
540pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
541extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
542
543579pub const WriteArchive = ZigLLVMWriteArchive;
544580extern fn ZigLLVMWriteArchive(
545581 archive_name: [*:0]const u8,