authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-25 22:38:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-25 22:38:50-07:00
log14d8a1c10da7e98ccfdbfd50473582043e48ca10
tree0b3e30ce2e327f5019f08f0ba2cc01f79c4f4225
parentc3d10dbda1fc133b7ca112787bbf0100b735ca36

stage2 llvm backend improvements working towards `zig test`

* properly set global variables to const if they are not a global variable. * implement global variable initializations. * initial implementation of llvmType() for structs and functions. * implement genTypedValue for variable tags * implement more AIR instructions: varptr, slice_ptr, slice_len, slice_elem_val, ptr_slice_elem_val, unwrap_errunion_payload, unwrap_errunion_payload_ptr, unwrap_errunion_err, unwrap_errunion_err_ptr.

3 files changed, 212 insertions(+), 33 deletions(-)

src/codegen/llvm.zig+190-31
...@@ -512,12 +512,15 @@ pub const DeclGen = struct {...@@ -512,12 +512,15 @@ pub const DeclGen = struct {
512512
513 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.513 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
514 const llvm_type = try self.llvmType(decl.ty);514 const llvm_type = try self.llvmType(decl.ty);
515 const val = try self.genTypedValue(.{ .ty = decl.ty, .val = decl.val }, null);
516 const global = self.llvmModule().addGlobal(llvm_type, decl.name);515 const global = self.llvmModule().addGlobal(llvm_type, decl.name);
517 llvm.setInitializer(global, val);516 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
517 const variable = payload.data;
518 global.setGlobalConstant(.False);
519 break :init_val variable.init;
520 } else decl.val;
518521
519 // TODO ask the Decl if it is const522 const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null);
520 // https://github.com/ziglang/zig/issues/7582523 llvm.setInitializer(global, llvm_init);
521524
522 return global;525 return global;
523 }526 }
...@@ -576,6 +579,36 @@ pub const DeclGen = struct {...@@ -576,6 +579,36 @@ pub const DeclGen = struct {
576 .ErrorSet => {579 .ErrorSet => {
577 return self.context.intType(16);580 return self.context.intType(16);
578 },581 },
582 .Struct => {
583 const struct_obj = t.castTag(.@"struct").?.data;
584 assert(struct_obj.haveFieldTypes());
585 const llvm_fields = try self.gpa.alloc(*const llvm.Type, struct_obj.fields.count());
586 defer self.gpa.free(llvm_fields);
587 for (struct_obj.fields.values()) |field, i| {
588 llvm_fields[i] = try self.llvmType(field.ty);
589 }
590 return self.context.structType(
591 llvm_fields.ptr,
592 @intCast(c_uint, llvm_fields.len),
593 .False,
594 );
595 },
596 .Fn => {
597 const ret_ty = try self.llvmType(t.fnReturnType());
598 const params_len = t.fnParamLen();
599 const llvm_params = try self.gpa.alloc(*const llvm.Type, params_len);
600 defer self.gpa.free(llvm_params);
601 for (llvm_params) |*llvm_param, i| {
602 llvm_param.* = try self.llvmType(t.fnParamType(i));
603 }
604 const is_var_args = t.fnIsVarArgs();
605 return llvm.functionType(
606 ret_ty,
607 llvm_params.ptr,
608 @intCast(c_uint, llvm_params.len),
609 llvm.Bool.fromBool(is_var_args),
610 );
611 },
579 .ComptimeInt => unreachable,612 .ComptimeInt => unreachable,
580 .ComptimeFloat => unreachable,613 .ComptimeFloat => unreachable,
581 .Type => unreachable,614 .Type => unreachable,
...@@ -586,10 +619,8 @@ pub const DeclGen = struct {...@@ -586,10 +619,8 @@ pub const DeclGen = struct {
586 .BoundFn => @panic("TODO remove BoundFn from the language"),619 .BoundFn => @panic("TODO remove BoundFn from the language"),
587620
588 .Float,621 .Float,
589 .Struct,
590 .Enum,622 .Enum,
591 .Union,623 .Union,
592 .Fn,
593 .Opaque,624 .Opaque,
594 .Frame,625 .Frame,
595 .AnyFrame,626 .AnyFrame,
...@@ -645,7 +676,11 @@ pub const DeclGen = struct {...@@ -645,7 +676,11 @@ pub const DeclGen = struct {
645 _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);676 _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca);
646 return alloca;677 return alloca;
647 },678 },
648 else => return self.todo("implement const of pointer type '{}'", .{tv.ty}),679 .variable => {
680 const variable = tv.val.castTag(.variable).?.data;
681 return self.resolveGlobalDecl(variable.owner_decl);
682 },
683 else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }),
649 },684 },
650 .Array => {685 .Array => {
651 if (tv.val.castTag(.bytes)) |payload| {686 if (tv.val.castTag(.bytes)) |payload| {
...@@ -765,45 +800,64 @@ pub const FuncGen = struct {...@@ -765,45 +800,64 @@ pub const FuncGen = struct {
765 const air_tags = self.air.instructions.items(.tag);800 const air_tags = self.air.instructions.items(.tag);
766 for (body) |inst| {801 for (body) |inst| {
767 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {802 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
803 // zig fmt: off
768 .add => try self.airAdd(inst),804 .add => try self.airAdd(inst),
769 .sub => try self.airSub(inst),805 .sub => try self.airSub(inst),
770806
771 .cmp_eq => try self.airCmp(inst, .eq),807 .cmp_eq => try self.airCmp(inst, .eq),
772 .cmp_gt => try self.airCmp(inst, .gt),808 .cmp_gt => try self.airCmp(inst, .gt),
773 .cmp_gte => try self.airCmp(inst, .gte),809 .cmp_gte => try self.airCmp(inst, .gte),
774 .cmp_lt => try self.airCmp(inst, .lt),810 .cmp_lt => try self.airCmp(inst, .lt),
775 .cmp_lte => try self.airCmp(inst, .lte),811 .cmp_lte => try self.airCmp(inst, .lte),
776 .cmp_neq => try self.airCmp(inst, .neq),812 .cmp_neq => try self.airCmp(inst, .neq),
777813
778 .is_non_null => try self.airIsNonNull(inst, false),814 .is_non_null => try self.airIsNonNull(inst, false),
779 .is_non_null_ptr => try self.airIsNonNull(inst, true),815 .is_non_null_ptr => try self.airIsNonNull(inst, true),
780 .is_null => try self.airIsNull(inst, false),816 .is_null => try self.airIsNull(inst, false),
781 .is_null_ptr => try self.airIsNull(inst, true),817 .is_null_ptr => try self.airIsNull(inst, true),
782818 .is_non_err => try self.airIsErr(inst, true, false),
783 .alloc => try self.airAlloc(inst),819 .is_non_err_ptr => try self.airIsErr(inst, true, true),
784 .arg => try self.airArg(inst),820 .is_err => try self.airIsErr(inst, false, false),
785 .bitcast => try self.airBitCast(inst),821 .is_err_ptr => try self.airIsErr(inst, false, true),
786 .block => try self.airBlock(inst),822
787 .br => try self.airBr(inst),823 .alloc => try self.airAlloc(inst),
824 .arg => try self.airArg(inst),
825 .bitcast => try self.airBitCast(inst),
826 .block => try self.airBlock(inst),
827 .br => try self.airBr(inst),
788 .breakpoint => try self.airBreakpoint(inst),828 .breakpoint => try self.airBreakpoint(inst),
789 .call => try self.airCall(inst),829 .call => try self.airCall(inst),
790 .cond_br => try self.airCondBr(inst),830 .cond_br => try self.airCondBr(inst),
791 .intcast => try self.airIntCast(inst),831 .intcast => try self.airIntCast(inst),
792 .ptrtoint => try self.airPtrToInt(inst),832 .ptrtoint => try self.airPtrToInt(inst),
793 .load => try self.airLoad(inst),833 .load => try self.airLoad(inst),
794 .loop => try self.airLoop(inst),834 .loop => try self.airLoop(inst),
795 .not => try self.airNot(inst),835 .not => try self.airNot(inst),
796 .ret => try self.airRet(inst),836 .ret => try self.airRet(inst),
797 .store => try self.airStore(inst),837 .store => try self.airStore(inst),
798 .unreach => self.airUnreach(inst),838 .assembly => try self.airAssembly(inst),
799 .optional_payload => try self.airOptionalPayload(inst, false),839 .varptr => try self.airVarPtr(inst),
840 .slice_ptr => try self.airSliceField(inst, 0),
841 .slice_len => try self.airSliceField(inst, 1),
842
843 .slice_elem_val => try self.airSliceElemVal(inst, false),
844 .ptr_slice_elem_val => try self.airSliceElemVal(inst, true),
845
846 .optional_payload => try self.airOptionalPayload(inst, false),
800 .optional_payload_ptr => try self.airOptionalPayload(inst, true),847 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
801 .assembly => try self.airAssembly(inst),848
849 .unwrap_errunion_payload => try self.airErrUnionPayload(inst, false),
850 .unwrap_errunion_payload_ptr => try self.airErrUnionPayload(inst, true),
851 .unwrap_errunion_err => try self.airErrUnionErr(inst, false),
852 .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
853
854 .unreach => self.airUnreach(inst),
802 .dbg_stmt => blk: {855 .dbg_stmt => blk: {
803 // TODO: implement debug info856 // TODO: implement debug info
804 break :blk null;857 break :blk null;
805 },858 },
806 else => |tag| return self.todo("implement AIR instruction: {}", .{tag}),859 else => |tag| return self.todo("implement AIR instruction: {}", .{tag}),
860 // zig fmt: on
807 };861 };
808 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val);862 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val);
809 }863 }
...@@ -986,6 +1040,52 @@ pub const FuncGen = struct {...@@ -986,6 +1040,52 @@ pub const FuncGen = struct {
986 return null;1040 return null;
987 }1041 }
9881042
1043 fn airVarPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1044 if (self.liveness.isUnused(inst))
1045 return null;
1046
1047 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1048 const variable = self.air.variables[ty_pl.payload];
1049 const decl_llvm_value = self.dg.resolveGlobalDecl(variable.owner_decl);
1050 return decl_llvm_value;
1051 }
1052
1053 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
1054 if (self.liveness.isUnused(inst))
1055 return null;
1056
1057 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1058 const operand = try self.resolveInst(ty_op.operand);
1059 return self.builder.buildExtractValue(operand, index, "");
1060 }
1061
1062 fn airSliceElemVal(
1063 self: *FuncGen,
1064 inst: Air.Inst.Index,
1065 operand_is_ptr: bool,
1066 ) !?*const llvm.Value {
1067 if (self.liveness.isUnused(inst))
1068 return null;
1069
1070 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1071 const lhs = try self.resolveInst(bin_op.lhs);
1072 const rhs = try self.resolveInst(bin_op.rhs);
1073
1074 const base_ptr = if (!operand_is_ptr) lhs else ptr: {
1075 const index_type = self.context.intType(32);
1076 const indices: [2]*const llvm.Value = .{
1077 index_type.constNull(),
1078 index_type.constInt(0, .False),
1079 };
1080 const ptr_field_ptr = self.builder.buildInBoundsGEP(lhs, &indices, 2, "");
1081 break :ptr self.builder.buildLoad(ptr_field_ptr, "");
1082 };
1083
1084 const indices: [1]*const llvm.Value = .{rhs};
1085 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, 1, "");
1086 return self.builder.buildLoad(ptr, "");
1087 }
1088
989 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1089 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
990 if (self.liveness.isUnused(inst))1090 if (self.liveness.isUnused(inst))
991 return null;1091 return null;
...@@ -1152,6 +1252,31 @@ pub const FuncGen = struct {...@@ -1152,6 +1252,31 @@ pub const FuncGen = struct {
1152 return self.builder.buildNot((try self.airIsNonNull(inst, operand_is_ptr)).?, "");1252 return self.builder.buildNot((try self.airIsNonNull(inst, operand_is_ptr)).?, "");
1153 }1253 }
11541254
1255 fn airIsErr(
1256 self: *FuncGen,
1257 inst: Air.Inst.Index,
1258 invert_logic: bool,
1259 operand_is_ptr: bool,
1260 ) !?*const llvm.Value {
1261 if (self.liveness.isUnused(inst))
1262 return null;
1263
1264 const un_op = self.air.instructions.items(.data)[inst].un_op;
1265 const operand = try self.resolveInst(un_op);
1266 const err_union_ty = self.air.typeOf(un_op);
1267 const payload_ty = err_union_ty.errorUnionPayload();
1268
1269 if (!payload_ty.hasCodeGenBits()) {
1270 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
1271 const op: llvm.IntPredicate = if (invert_logic) .EQ else .NE;
1272 const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror));
1273 const zero = err_set_ty.constNull();
1274 return self.builder.buildICmp(op, loaded, zero, "");
1275 }
1276
1277 return self.todo("implement 'airIsErr' for error unions with nonzero payload", .{});
1278 }
1279
1155 fn airOptionalPayload(1280 fn airOptionalPayload(
1156 self: *FuncGen,1281 self: *FuncGen,
1157 inst: Air.Inst.Index,1282 inst: Air.Inst.Index,
...@@ -1177,6 +1302,40 @@ pub const FuncGen = struct {...@@ -1177,6 +1302,40 @@ pub const FuncGen = struct {
1177 }1302 }
1178 }1303 }
11791304
1305 fn airErrUnionPayload(
1306 self: *FuncGen,
1307 inst: Air.Inst.Index,
1308 operand_is_ptr: bool,
1309 ) !?*const llvm.Value {
1310 if (self.liveness.isUnused(inst))
1311 return null;
1312
1313 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1314 const operand = try self.resolveInst(ty_op.operand);
1315 const err_union_ty = self.air.typeOf(ty_op.operand);
1316 const payload_ty = err_union_ty.errorUnionPayload();
1317
1318 if (!payload_ty.hasCodeGenBits()) {
1319 return null;
1320 }
1321
1322 _ = operand;
1323 _ = operand_is_ptr;
1324 return self.todo("implement 'airErrUnionPayload' for type {}", .{self.air.typeOf(ty_op.operand)});
1325 }
1326
1327 fn airErrUnionErr(
1328 self: *FuncGen,
1329 inst: Air.Inst.Index,
1330 operand_is_ptr: bool,
1331 ) !?*const llvm.Value {
1332 if (self.liveness.isUnused(inst))
1333 return null;
1334
1335 _ = operand_is_ptr;
1336 return self.todo("implement 'airErrUnionErr'", .{});
1337 }
1338
1180 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1339 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1181 if (self.liveness.isUnused(inst))1340 if (self.liveness.isUnused(inst))
1182 return null;1341 return null;
src/codegen/llvm/bindings.zig+21-1
...@@ -42,6 +42,9 @@ pub const Context = opaque {...@@ -42,6 +42,9 @@ pub const Context = opaque {
42 Packed: Bool,42 Packed: Bool,
43 ) *const Type;43 ) *const Type;
4444
45 const structCreateNamed = LLVMStructCreateNamed;
46 extern fn LLVMStructCreateNamed(C: *const Context, Name: [*:0]const u8) *const Type;
47
45 pub const constString = LLVMConstStringInContext;48 pub const constString = LLVMConstStringInContext;
46 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;49 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;
4750
...@@ -76,6 +79,9 @@ pub const Value = opaque {...@@ -76,6 +79,9 @@ pub const Value = opaque {
7679
77 pub const typeOf = LLVMTypeOf;80 pub const typeOf = LLVMTypeOf;
78 extern fn LLVMTypeOf(Val: *const Value) *const Type;81 extern fn LLVMTypeOf(Val: *const Value) *const Type;
82
83 pub const setGlobalConstant = LLVMSetGlobalConstant;
84 extern fn LLVMSetGlobalConstant(GlobalVar: *const Value, IsConstant: Bool) void;
79};85};
8086
81pub const Type = opaque {87pub const Type = opaque {
...@@ -99,6 +105,14 @@ pub const Type = opaque {...@@ -99,6 +105,14 @@ pub const Type = opaque {
99105
100 pub const arrayType = LLVMArrayType;106 pub const arrayType = LLVMArrayType;
101 extern fn LLVMArrayType(ElementType: *const Type, ElementCount: c_uint) *const Type;107 extern fn LLVMArrayType(ElementType: *const Type, ElementCount: c_uint) *const Type;
108
109 pub const structSetBody = LLVMStructSetBody;
110 extern fn LLVMStructSetBody(
111 StructTy: *const Type,
112 ElementTypes: [*]*const Type,
113 ElementCount: c_uint,
114 Packed: Bool,
115 ) void;
102};116};
103117
104pub const Module = opaque {118pub const Module = opaque {
...@@ -257,7 +271,13 @@ pub const Builder = opaque {...@@ -257,7 +271,13 @@ pub const Builder = opaque {
257 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;271 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
258272
259 pub const buildInBoundsGEP = LLVMBuildInBoundsGEP;273 pub const buildInBoundsGEP = LLVMBuildInBoundsGEP;
260 extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value;274 extern fn LLVMBuildInBoundsGEP(
275 B: *const Builder,
276 Pointer: *const Value,
277 Indices: [*]const *const Value,
278 NumIndices: c_uint,
279 Name: [*:0]const u8,
280 ) *const Value;
261281
262 pub const buildICmp = LLVMBuildICmp;282 pub const buildICmp = LLVMBuildICmp;
263 extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;283 extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
src/type.zig+1-1
...@@ -915,7 +915,7 @@ pub const Type = extern union {...@@ -915,7 +915,7 @@ pub const Type = extern union {
915 }915 }
916 try writer.writeAll(") callconv(.");916 try writer.writeAll(") callconv(.");
917 try writer.writeAll(@tagName(payload.cc));917 try writer.writeAll(@tagName(payload.cc));
918 try writer.writeAll(")");918 try writer.writeAll(") ");
919 ty = payload.return_type;919 ty = payload.return_type;
920 continue;920 continue;
921 },921 },