// Allocates a literal array on the stack func (v *Codegen) genArrayLiteral(n *parser.ArrayLiteral) llvm.Value { memberLLVMType := v.typeToLLVMType(n.Type.(parser.ArrayType).MemberType) // allocate backing array arrAlloca := v.builder.CreateArrayAlloca(llvm.ArrayType(memberLLVMType, len(n.Members)), llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false), "") // allocate the array object structAlloca := v.builder.CreateAlloca(v.typeToLLVMType(n.Type), "") // set the length of the array lenGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 0, false)}, "") v.builder.CreateStore(llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false), lenGEP) // set the array pointer to the backing array we allocated arrGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 1, false)}, "") v.builder.CreateStore(v.builder.CreateBitCast(arrAlloca, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0), ""), arrGEP) // copy the constant array to the backing array arrConstVals := make([]llvm.Value, 0, len(n.Members)) for _, mem := range n.Members { arrConstVals = append(arrConstVals, v.genExpr(mem)) } arrConst := llvm.ConstArray(llvm.ArrayType(memberLLVMType, len(n.Members)), arrConstVals) v.builder.CreateStore(arrConst, arrAlloca) return v.builder.CreateLoad(structAlloca, "") }
func primitiveTypeToLLVMType(typ parser.PrimitiveType) llvm.Type { switch typ { case parser.PRIMITIVE_int, parser.PRIMITIVE_uint: return llvm.IntType(intSize * 8) case parser.PRIMITIVE_s8, parser.PRIMITIVE_u8: return llvm.IntType(8) case parser.PRIMITIVE_s16, parser.PRIMITIVE_u16: return llvm.IntType(16) case parser.PRIMITIVE_s32, parser.PRIMITIVE_u32: return llvm.IntType(32) case parser.PRIMITIVE_s64, parser.PRIMITIVE_u64: return llvm.IntType(64) case parser.PRIMITIVE_i128, parser.PRIMITIVE_u128: return llvm.IntType(128) case parser.PRIMITIVE_f32: return llvm.FloatType() case parser.PRIMITIVE_f64: return llvm.DoubleType() case parser.PRIMITIVE_f128: return llvm.FP128Type() case parser.PRIMITIVE_rune: // runes are signed 32-bit int return llvm.IntType(32) case parser.PRIMITIVE_bool: return llvm.IntType(1) case parser.PRIMITIVE_str: return llvm.PointerType(llvm.IntType(8), 0) default: panic("Unimplemented primitive type in LLVM codegen") } }
func (v *Codegen) enumTypeToLLVMTypeFields(typ parser.EnumType) []llvm.Type { longestLength := uint64(0) for _, member := range typ.Members { memLength := v.targetData.TypeAllocSize(v.typeToLLVMType(member.Type)) if memLength > longestLength { longestLength = memLength } } // TODO: verify no overflow return []llvm.Type{llvm.IntType(32), llvm.ArrayType(llvm.IntType(8), int(longestLength))} }
// Allocates a literal array on the stack func (v *Codegen) genArrayLiteral(n *parser.ArrayLiteral) llvm.Value { arrayLLVMType := v.typeToLLVMType(n.Type) memberLLVMType := v.typeToLLVMType(n.Type.(parser.ArrayType).MemberType) if v.inFunction { // allocate backing array arrAlloca := v.builder.CreateAlloca(llvm.ArrayType(memberLLVMType, len(n.Members)), "") // copy the constant array to the backing array for idx, value := range n.Members { gep := v.builder.CreateGEP(arrAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), uint64(idx), false)}, "") value := v.genExpr(value) v.builder.CreateStore(value, gep) } // allocate struct structAlloca := v.builder.CreateAlloca(arrayLLVMType, "") // set the length of the array lenGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 0, false)}, "") v.builder.CreateStore(llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false), lenGEP) // set the array pointer to the backing array we allocated arrGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 1, false)}, "") v.builder.CreateStore(v.builder.CreateBitCast(arrAlloca, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0), ""), arrGEP) return v.builder.CreateLoad(structAlloca, "") } else { backName := fmt.Sprintf("_globarr_back_%d", v.arrayIndex) v.arrayIndex++ backGlob := llvm.AddGlobal(v.curFile.Module, llvm.ArrayType(memberLLVMType, len(n.Members)), backName) backGlob.SetLinkage(llvm.InternalLinkage) backGlob.SetGlobalConstant(false) arrConstVals := make([]llvm.Value, len(n.Members)) for idx, mem := range n.Members { value := v.genExpr(mem) if !value.IsConstant() { v.err("Encountered non-constant value in global array") } arrConstVals[idx] = v.genExpr(mem) } backGlob.SetInitializer(llvm.ConstArray(memberLLVMType, arrConstVals)) lengthVal := llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false) backRef := llvm.ConstBitCast(backGlob, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0)) return llvm.ConstStruct([]llvm.Value{lengthVal, backRef}, false) } }
func (v *Codegen) genLogicalBinop(n *parser.BinaryExpr) llvm.Value { and := n.Op == parser.BINOP_LOG_AND next := llvm.AddBasicBlock(v.currentLLVMFunction(), "and_next") exit := llvm.AddBasicBlock(v.currentLLVMFunction(), "and_exit") b1 := v.genExpr(n.Lhand) first := v.builder().GetInsertBlock() if and { v.builder().CreateCondBr(b1, next, exit) } else { v.builder().CreateCondBr(b1, exit, next) } v.builder().SetInsertPointAtEnd(next) b2 := v.genExpr(n.Rhand) next = v.builder().GetInsertBlock() v.builder().CreateBr(exit) v.builder().SetInsertPointAtEnd(exit) phi := v.builder().CreatePHI(b2.Type(), "and_phi") var testIncVal uint64 if and { testIncVal = 0 } else { testIncVal = 1 } phi.AddIncoming([]llvm.Value{llvm.ConstInt(llvm.IntType(1), testIncVal, false), b2}, []llvm.BasicBlock{first, next}) return phi }
func (v *Codegen) enumTypeToLLVMType(typ parser.EnumType) llvm.Type { if typ.Simple { // TODO: Handle other integer size, maybe dynamic depending on max value? (1 / 2) return llvm.IntType(32) } return llvm.StructType(v.enumTypeToLLVMTypeFields(typ), false) }
func (v *Codegen) primitiveTypeToLLVMType(typ parser.PrimitiveType) llvm.Type { switch typ { case parser.PRIMITIVE_int, parser.PRIMITIVE_uint: return v.targetData.IntPtrType() case parser.PRIMITIVE_s8, parser.PRIMITIVE_u8: return llvm.IntType(8) case parser.PRIMITIVE_s16, parser.PRIMITIVE_u16: return llvm.IntType(16) case parser.PRIMITIVE_s32, parser.PRIMITIVE_u32: return llvm.IntType(32) case parser.PRIMITIVE_s64, parser.PRIMITIVE_u64: return llvm.IntType(64) case parser.PRIMITIVE_s128, parser.PRIMITIVE_u128: return llvm.IntType(128) case parser.PRIMITIVE_f32: return llvm.FloatType() case parser.PRIMITIVE_f64: return llvm.DoubleType() case parser.PRIMITIVE_f128: return llvm.FP128Type() case parser.PRIMITIVE_rune: // runes are signed 32-bit int return llvm.IntType(32) case parser.PRIMITIVE_bool: return llvm.IntType(1) case parser.PRIMITIVE_void: return llvm.VoidType() default: panic("Unimplemented primitive type in LLVM codegen") } }
func (v *Codegen) genArrayLenExpr(n *parser.ArrayLenExpr) llvm.Value { if arrayLit, ok := n.Expr.(*parser.CompositeLiteral); ok { arrayLen := len(arrayLit.Values) return llvm.ConstInt(llvm.IntType(64), uint64(arrayLen), false) } gep := v.genAccessGEP(n.Expr) gep = v.builder().CreateLoad(v.builder().CreateStructGEP(gep, 0, ""), "") return gep }
func (v *Codegen) enumTypeToLLVMType(typ *parser.EnumType) llvm.Type { if typ.Simple { // TODO: Handle other integer size, maybe dynamic depending on max value? (1 / 2) return llvm.IntType(32) } if t, ok := v.enumLookup_UseHelperFunction[typ]; ok { return t } return llvm.StructType(v.enumTypeToLLVMTypeFields(typ), false) }
func (v *Codegen) genEnumLiteral(n *parser.EnumLiteral) llvm.Value { enumType := n.Type.ActualType().(parser.EnumType) enumLLVMType := v.typeToLLVMType(n.Type) memberIdx := enumType.MemberIndex(n.Member) member := enumType.Members[memberIdx] if enumType.Simple { return llvm.ConstInt(enumLLVMType, uint64(member.Tag), false) } // TODO: Handle other integer size, maybe dynamic depending on max value? tagValue := llvm.ConstInt(llvm.IntType(32), uint64(member.Tag), false) enumValue := llvm.Undef(enumLLVMType) enumValue = v.builder().CreateInsertValue(enumValue, tagValue, 0, "") memberLLVMType := v.typeToLLVMType(member.Type) var memberValue llvm.Value if n.TupleLiteral != nil { memberValue = v.genTupleLiteral(n.TupleLiteral) } else if n.CompositeLiteral != nil { memberValue = v.genCompositeLiteral(n.CompositeLiteral) } if v.inFunction() { alloc := v.builder().CreateAlloca(enumLLVMType, "") tagGep := v.builder().CreateStructGEP(alloc, 0, "") v.builder().CreateStore(tagValue, tagGep) if !memberValue.IsNil() { dataGep := v.builder().CreateStructGEP(alloc, 1, "") dataGep = v.builder().CreateBitCast(dataGep, llvm.PointerType(memberLLVMType, 0), "") v.builder().CreateStore(memberValue, dataGep) } return v.builder().CreateLoad(alloc, "") } else { panic("unimplemented: global enum literal") } }
// Allocates a literal array on the stack func (v *Codegen) genArrayLiteral(n *parser.ArrayLiteral) llvm.Value { arrayLLVMType := v.typeToLLVMType(n.Type) memberLLVMType := v.typeToLLVMType(n.Type.(parser.ArrayType).MemberType) arrayValues := make([]llvm.Value, len(n.Members)) for idx, mem := range n.Members { value := v.genExpr(mem) if !v.inFunction && !value.IsConstant() { v.err("Encountered non-constant value in global array") } arrayValues[idx] = value } lengthValue := llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false) var backingArrayPointer llvm.Value if v.inFunction { // allocate backing array backingArray := v.builder.CreateAlloca(llvm.ArrayType(memberLLVMType, len(n.Members)), "") // copy the constant array to the backing array for idx, value := range arrayValues { gep := v.builder.CreateStructGEP(backingArray, idx, "") v.builder.CreateStore(value, gep) } backingArrayPointer = v.builder.CreateBitCast(backingArray, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0), "") } else { backName := fmt.Sprintf("_globarr_back_%d", v.arrayIndex) v.arrayIndex++ backingArray := llvm.AddGlobal(v.curFile.Module, llvm.ArrayType(memberLLVMType, len(n.Members)), backName) backingArray.SetLinkage(llvm.InternalLinkage) backingArray.SetGlobalConstant(false) backingArray.SetInitializer(llvm.ConstArray(memberLLVMType, arrayValues)) backingArrayPointer = llvm.ConstBitCast(backingArray, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0)) } structValue := llvm.Undef(arrayLLVMType) structValue = v.builder.CreateInsertValue(structValue, lengthValue, 0, "") structValue = v.builder.CreateInsertValue(structValue, backingArrayPointer, 1, "") return structValue }
func (v *Codegen) addEnumType(typ parser.EnumType, name string) { if _, ok := v.namedTypeLookup[name]; ok { return } if typ.Simple { // TODO: Handle other integer size, maybe dynamic depending on max value? v.namedTypeLookup[name] = llvm.IntType(32) } else { enum := v.curFile.LlvmModule.Context().StructCreateNamed(name) v.namedTypeLookup[name] = enum for _, member := range typ.Members { if named, ok := member.Type.(*parser.NamedType); ok { v.addNamedType(named) } } enum.StructSetBody(v.enumTypeToLLVMTypeFields(typ), false) } }
func (v *Codegen) arrayTypeToLLVMType(typ parser.ArrayType) llvm.Type { fields := []llvm.Type{llvm.IntType(32), llvm.PointerType(llvm.ArrayType(v.typeToLLVMType(typ.MemberType), 0), 0)} return llvm.StructType(fields, false) }
func (v *Codegen) genVariableDecl(n *parser.VariableDecl, semicolon bool) llvm.Value { var res llvm.Value if v.inFunction { mangledName := n.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE) funcEntry := v.currentFunction.EntryBasicBlock() // use this builder for the variable alloca // this means all allocas go at the start of the function // so each variable is only allocated once allocBuilder := llvm.NewBuilder() if funcEntry == v.builder.GetInsertBlock() { allocBuilder.SetInsertPointAtEnd(funcEntry) } else { allocBuilder.SetInsertPointBefore(funcEntry.LastInstruction()) } alloc := allocBuilder.CreateAlloca(v.typeToLLVMType(n.Variable.Type), mangledName) // set allocated memory to zero fn := v.curFile.Module.NamedFunction("llvm.memset.p0i8.i32") if fn.IsNil() { fnType := llvm.FunctionType(llvm.VoidType(), []llvm.Type{llvm.PointerType(llvm.IntType(8), 0), llvm.IntType(8), llvm.IntType(32), llvm.IntType(32), llvm.IntType(1)}, false) fn = llvm.AddFunction(v.curFile.Module, "llvm.memset.p0i8.i32", fnType) } // cast alloc to byte array castAlloc := allocBuilder.CreateBitCast(alloc, llvm.PointerType(llvm.IntType(8), 0), "") // get type length gep := allocBuilder.CreateGEP(llvm.ConstNull(llvm.PointerType(v.typeToLLVMType(n.Variable.Type), 0)), []llvm.Value{llvm.ConstInt(llvm.IntType(32), 1, false)}, "") length := allocBuilder.CreatePtrToInt(gep, llvm.IntType(32), "") // call memset intrinsic allocBuilder.CreateCall(fn, []llvm.Value{castAlloc, llvm.ConstInt(llvm.IntType(8), 0, false), length, llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(1), 0, false)}, "") allocBuilder.Dispose() v.variableLookup[n.Variable] = alloc if n.Assignment != nil { if value := v.genExpr(n.Assignment); !value.IsNil() { v.builder.CreateStore(value, alloc) } } } else { mangledName := n.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE) varType := v.typeToLLVMType(n.Variable.Type) value := llvm.AddGlobal(v.curFile.Module, varType, mangledName) value.SetLinkage(llvm.InternalLinkage) value.SetGlobalConstant(!n.Variable.Mutable) if n.Assignment != nil { value.SetInitializer(v.genExpr(n.Assignment)) } v.variableLookup[n.Variable] = value } return res }
func (v *Codegen) genSizeofExpr(n *parser.SizeofExpr) llvm.Value { if n.Expr != nil { gep := v.builder.CreateGEP(llvm.ConstNull(llvm.PointerType(v.typeToLLVMType(n.Expr.GetType()), 0)), []llvm.Value{llvm.ConstInt(llvm.IntType(32), 1, false)}, "") return v.builder.CreatePtrToInt(gep, v.typeToLLVMType(n.GetType()), "sizeof") } else { // we have a type panic("can't do this yet") } }