| ... | @@ -512,12 +512,15 @@ pub const DeclGen = struct { | ... | @@ -512,12 +512,15 @@ pub const DeclGen = struct { |
| 512 | | 512 | |
| 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; |
| 518 | | 521 | |
| 519 | // TODO ask the Decl if it is const | 522 | const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null); |
| 520 | // https://github.com/ziglang/zig/issues/7582 | 523 | llvm.setInitializer(global, llvm_init); |
| 521 | | 524 | |
| 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"), |
| 587 | | 620 | |
| 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), |
| 770 | | 806 | |
| 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), |
| 777 | | 813 | |
| 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), |
| 782 | | 818 | .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 info | 856 | // 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 | } |
| 988 | | 1042 | |
| | 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 | } |
| 1154 | | 1254 | |
| | 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 | } |
| 1179 | | 1304 | |
| | 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; |