func (v *Codegen) genBoundsCheck(limit llvm.Value, index llvm.Value, indexType parser.Type) { segvBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_segv") endBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_end") upperCheckBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_upper_block") tooLow := v.builder().CreateICmp(llvm.IntSGT, llvm.ConstInt(index.Type(), 0, false), index, "boundscheck_lower") v.builder().CreateCondBr(tooLow, segvBlock, upperCheckBlock) v.builder().SetInsertPointAtEnd(upperCheckBlock) // make sure limit and index have same width castedLimit := limit castedIndex := index if index.Type().IntTypeWidth() < limit.Type().IntTypeWidth() { if indexType.IsSigned() { castedIndex = v.builder().CreateSExt(index, limit.Type(), "") } else { castedIndex = v.builder().CreateZExt(index, limit.Type(), "") } } else if index.Type().IntTypeWidth() > limit.Type().IntTypeWidth() { castedLimit = v.builder().CreateZExt(limit, index.Type(), "") } tooHigh := v.builder().CreateICmp(llvm.IntSLE, castedLimit, castedIndex, "boundscheck_upper") v.builder().CreateCondBr(tooHigh, segvBlock, endBlock) v.builder().SetInsertPointAtEnd(segvBlock) v.genRaiseSegfault() v.builder().CreateUnreachable() v.builder().SetInsertPointAtEnd(endBlock) }
func (v *RecursiveDefinitionCheck) Visit(s *SemanticAnalyzer, n parser.Node) { var typ parser.Type if typeDecl, ok := n.(*parser.TypeDecl); ok { actualType := typeDecl.NamedType.ActualType() switch actualType.(type) { case *parser.EnumType: typ = actualType.(*parser.EnumType) case *parser.StructType: typ = actualType.(*parser.StructType) // TODO: Check tuple types once we add named types for everything default: return } } if ok, path := isTypeRecursive(typ); ok { s.Err(n, "Encountered recursive type definition") log.Errorln("semantic", "Path taken:") for _, typ := range path { log.Error("semantic", typ.TypeName()) log.Error("semantic", " <- ") } log.Error("semantic", "%s\n\n", typ.TypeName()) } }
func createStructInitializer(typ parser.Type) *parser.CompositeLiteral { lit := &parser.CompositeLiteral{Type: typ} hasDefaultValues := false structType := typ.ActualType().(parser.StructType) for _, decl := range structType.Variables { vari := decl.Variable var value parser.Expr if _, ok := vari.Type.ActualType().(parser.StructType); ok { value = createStructInitializer(vari.Type) } else { value = decl.Assignment } if value != nil { hasDefaultValues = true lit.Values = append(lit.Values, value) lit.Fields = append(lit.Fields, vari.Name) } } if hasDefaultValues { return lit } return nil }
func (v *Codegen) genDefaultValue(typ parser.Type) llvm.Value { atyp := typ.ActualType() // Generate default struct values if structType, ok := atyp.(parser.StructType); ok { lit := createStructInitializer(typ) if lit != nil { return v.genStructLiteral(lit) } else { return llvm.Undef(v.typeToLLVMType(structType)) } } if tupleType, ok := atyp.(parser.TupleType); ok { values := make([]llvm.Value, len(tupleType.Members)) for idx, member := range tupleType.Members { values[idx] = v.genDefaultValue(member) } return llvm.ConstStruct(values, false) } if atyp.IsIntegerType() || atyp == parser.PRIMITIVE_bool { return llvm.ConstInt(v.typeToLLVMType(atyp), 0, false) } if atyp.IsFloatingType() { return llvm.ConstFloat(v.typeToLLVMType(atyp), 0) } panic("type does not have default value: " + atyp.TypeName()) }
func createStructInitializer(typ parser.Type) *parser.StructLiteral { lit := &parser.StructLiteral{Type: typ, Values: make(map[string]parser.Expr)} hasDefaultValues := false structType := typ.ActualType().(parser.StructType) for _, decl := range structType.Variables { vari := decl.Variable var value parser.Expr if _, ok := vari.Type.ActualType().(parser.StructType); ok { value = createStructInitializer(vari.Type) } else { value = decl.Assignment } if value != nil { hasDefaultValues = true lit.Values[vari.Name] = value } } if hasDefaultValues { return lit } return nil }
func isTypeRecursive(typ parser.Type) (bool, []parser.Type) { typ = typ.ActualType() var check func(current parser.Type, path *[]parser.Type, traversed map[parser.Type]bool) bool check = func(current parser.Type, path *[]parser.Type, traversed map[parser.Type]bool) bool { switch current.(type) { case *parser.NamedType: if traversed[current] { return true } traversed[current] = true } switch current.(type) { case parser.StructType: st := current.(parser.StructType) for _, decl := range st.Variables { if check(decl.Variable.Type, path, traversed) { *path = append(*path, decl.Variable.Type) return true } } case parser.TupleType: tt := current.(parser.TupleType) for _, mem := range tt.Members { if check(mem, path, traversed) { *path = append(*path, mem) return true } } case parser.EnumType: et := current.(parser.EnumType) for _, mem := range et.Members { if check(mem.Type, path, traversed) { *path = append(*path, mem.Type) return true } } case *parser.NamedType: nt := current.(*parser.NamedType) if check(nt.Type, path, traversed) { *path = append(*path, nt.Type) return true } // TODO: Add array if we ever add embedded fixed size/static arrays } return false } var path []parser.Type return check(typ, &path, make(map[parser.Type]bool)), path }
func (v *RecursiveDefinitionCheck) Visit(s *SemanticAnalyzer, n parser.Node) { var typ parser.Type if typeDecl, ok := n.(*parser.TypeDecl); ok { typ = typeDecl.NamedType } else { return } if ok, path := isTypeRecursive(typ); ok { s.Err(n, "Encountered recursive type definition") log.Errorln("semantic", "Path taken:") for _, typ := range path { log.Error("semantic", typ.TypeName()) log.Error("semantic", " <- ") } log.Error("semantic", "%s\n\n", typ.TypeName()) } }
func (v *Codegen) typeToLLVMType(typ parser.Type) llvm.Type { switch typ := typ.(type) { case parser.PrimitiveType: return v.primitiveTypeToLLVMType(typ) case parser.FunctionType: return v.functionTypeToLLVMType(typ, true) case parser.StructType: return v.structTypeToLLVMType(typ) case parser.PointerType: return llvm.PointerType(v.typeToLLVMType(typ.Addressee), 0) case parser.ArrayType: return v.arrayTypeToLLVMType(typ) case parser.TupleType: return v.tupleTypeToLLVMType(typ) case parser.EnumType: return v.enumTypeToLLVMType(typ) case *parser.NamedType: nt := typ switch nt.Type.(type) { case parser.StructType, parser.EnumType: v.addNamedType(nt) lt := v.namedTypeLookup[nt.MangledName(parser.MANGLE_ARK_UNSTABLE)] return lt default: return v.typeToLLVMType(nt.Type) } case parser.MutableReferenceType: return llvm.PointerType(v.typeToLLVMType(typ.Referrer), 0) case parser.ConstantReferenceType: return llvm.PointerType(v.typeToLLVMType(typ.Referrer), 0) default: log.Debugln("codegen", "Type was %s (%s)", typ.TypeName(), reflect.TypeOf(typ)) panic("Unimplemented type category in LLVM codegen") } }
func (v *Codegen) genBinop(operator parser.BinOpType, resType, lhandType, rhandType parser.Type, lhand, rhand llvm.Value) llvm.Value { if lhand.IsNil() || rhand.IsNil() { v.err("invalid binary expr") } else { switch operator { // Arithmetic case parser.BINOP_ADD: if resType.IsFloatingType() { return v.builder().CreateFAdd(lhand, rhand, "") } else { return v.builder().CreateAdd(lhand, rhand, "") } case parser.BINOP_SUB: if resType.IsFloatingType() { return v.builder().CreateFSub(lhand, rhand, "") } else { return v.builder().CreateSub(lhand, rhand, "") } case parser.BINOP_MUL: if resType.IsFloatingType() { return v.builder().CreateFMul(lhand, rhand, "") } else { return v.builder().CreateMul(lhand, rhand, "") } case parser.BINOP_DIV: if resType.IsFloatingType() { return v.builder().CreateFDiv(lhand, rhand, "") } else { if resType.(parser.PrimitiveType).IsSigned() { return v.builder().CreateSDiv(lhand, rhand, "") } else { return v.builder().CreateUDiv(lhand, rhand, "") } } case parser.BINOP_MOD: if resType.IsFloatingType() { return v.builder().CreateFRem(lhand, rhand, "") } else { if resType.(parser.PrimitiveType).IsSigned() { return v.builder().CreateSRem(lhand, rhand, "") } else { return v.builder().CreateURem(lhand, rhand, "") } } // Comparison case parser.BINOP_GREATER, parser.BINOP_LESS, parser.BINOP_GREATER_EQ, parser.BINOP_LESS_EQ, parser.BINOP_EQ, parser.BINOP_NOT_EQ: if lhandType.IsFloatingType() { return v.builder().CreateFCmp(comparisonOpToFloatPredicate(operator), lhand, rhand, "") } else { return v.builder().CreateICmp(comparisonOpToIntPredicate(operator, lhandType.IsSigned()), lhand, rhand, "") } // Bitwise case parser.BINOP_BIT_AND: return v.builder().CreateAnd(lhand, rhand, "") case parser.BINOP_BIT_OR: return v.builder().CreateOr(lhand, rhand, "") case parser.BINOP_BIT_XOR: return v.builder().CreateXor(lhand, rhand, "") case parser.BINOP_BIT_LEFT: return v.builder().CreateShl(lhand, rhand, "") case parser.BINOP_BIT_RIGHT: // TODO make sure both operands are same type (create type cast here?) // TODO in semantic.go, make sure rhand is *unsigned* (LLVM always treats it that way) // TODO doc this if lhandType.IsSigned() { return v.builder().CreateAShr(lhand, rhand, "") } else { return v.builder().CreateLShr(lhand, rhand, "") } default: panic("umimplented binop") } } panic("unreachable") }