func (lhs *LLVMValue) BinaryOp(op token.Token, rhs_ Value) Value { if op == token.NEQ { result := lhs.BinaryOp(token.EQL, rhs_) return result.UnaryOp(token.NOT) } var result llvm.Value c := lhs.compiler b := lhs.compiler.builder // Later we can do better by treating constants specially. For now, let's // convert to LLVMValue's. var rhs *LLVMValue rhsisnil := false switch rhs_ := rhs_.(type) { case *LLVMValue: rhs = rhs_ case NilValue: rhsisnil = true switch rhs_ := rhs_.Convert(lhs.Type()).(type) { case ConstValue: rhs = c.NewLLVMValue(rhs_.LLVMValue(), rhs_.Type()) case *LLVMValue: rhs = rhs_ } case ConstValue: value := rhs_.Convert(lhs.Type()) rhs = c.NewLLVMValue(value.LLVMValue(), value.Type()) } switch typ := types.Underlying(lhs.typ).(type) { case *types.Struct: // TODO check types are the same. element_types_count := lhs.LLVMValue().Type().StructElementTypesCount() struct_fields := typ.Fields if element_types_count > 0 { t := c.ObjGetType(struct_fields[0]) first_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), t) first_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), 0, ""), t) first := first_lhs.BinaryOp(op, first_rhs) logical_op := token.LAND if op == token.NEQ { logical_op = token.LOR } result := first for i := 1; i < element_types_count; i++ { t := c.ObjGetType(struct_fields[i]) next_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) next_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) next := next_lhs.BinaryOp(op, next_rhs) result = result.BinaryOp(logical_op, next) } return result } case *types.Interface: if rhsisnil { valueNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result := b.CreateAnd(typeNull, valueNull, "") return c.NewLLVMValue(result, types.Bool) } // TODO check for interface/interface comparison vs. interface/value comparison. return lhs.compareI2I(rhs) case *types.Slice: // []T == nil isnil := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") return c.NewLLVMValue(isnil, types.Bool) } // Strings. if types.Underlying(lhs.typ) == types.String { if types.Underlying(rhs.typ) == types.String { switch op { case token.ADD: return c.concatenateStrings(lhs, rhs) case token.EQL, token.LSS, token.GTR, token.LEQ, token.GEQ: return c.compareStrings(lhs, rhs, op) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } } panic("unimplemented") } // Determine whether to use integer or floating point instructions. // TODO determine the NaN rules. isfp := types.Identical(types.Underlying(lhs.typ), types.Float32) || types.Identical(types.Underlying(lhs.typ), types.Float64) switch op { case token.MUL: if isfp { result = b.CreateFMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.QUO: if isfp { result = b.CreateFDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.REM: if isfp { result = b.CreateFRem(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateURem(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.ADD: if isfp { result = b.CreateFAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SUB: if isfp { result = b.CreateFSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SHL: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) result = b.CreateShl(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SHR: rhs = rhs.Convert(lhs.Type()).(*LLVMValue) result = b.CreateAShr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.NEQ: if isfp { result = b.CreateFCmp(llvm.FloatONE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntNE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.EQL: if isfp { result = b.CreateFCmp(llvm.FloatOEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LSS: if isfp { result = b.CreateFCmp(llvm.FloatOLT, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LEQ: // TODO signed/unsigned if isfp { result = b.CreateFCmp(llvm.FloatOLE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GTR: if isfp { result = b.CreateFCmp(llvm.FloatOGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GEQ: if isfp { result = b.CreateFCmp(llvm.FloatOGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.AND: // a & b result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.OR: // a | b result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.XOR: // a ^ b result = b.CreateXor(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } panic("unreachable") }
func (lhs *LLVMValue) BinaryOp(op token.Token, rhs_ Value) Value { if op == token.NEQ { result := lhs.BinaryOp(token.EQL, rhs_) return result.UnaryOp(token.NOT) } var result llvm.Value c := lhs.compiler b := lhs.compiler.builder // Later we can do better by treating constants specially. For now, let's // convert to LLVMValue's. var rhs *LLVMValue switch rhs_ := rhs_.(type) { case *LLVMValue: rhs = rhs_ case NilValue: switch rhs_ := rhs_.Convert(lhs.Type()).(type) { case ConstValue: rhs = c.NewLLVMValue(rhs_.LLVMValue(), rhs_.Type()) case *LLVMValue: rhs = rhs_ } case ConstValue: value := rhs_.Convert(lhs.Type()) rhs = c.NewLLVMValue(value.LLVMValue(), value.Type()) } // Special case for structs. // TODO handle strings as an even more special case. if struct_type, ok := types.Underlying(lhs.typ).(*types.Struct); ok { // TODO check types are the same. element_types_count := lhs.LLVMValue().Type().StructElementTypesCount() struct_fields := struct_type.Fields if element_types_count > 0 { t := c.ObjGetType(struct_fields[0]) first_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), t) first_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), 0, ""), t) first := first_lhs.BinaryOp(op, first_rhs) logical_op := token.LAND if op == token.NEQ { logical_op = token.LOR } result := first for i := 1; i < element_types_count; i++ { t := c.ObjGetType(struct_fields[i]) next_lhs := c.NewLLVMValue(b.CreateExtractValue(lhs.LLVMValue(), i, ""), t) next_rhs := c.NewLLVMValue(b.CreateExtractValue(rhs.LLVMValue(), i, ""), t) next := next_lhs.BinaryOp(op, next_rhs) result = result.BinaryOp(logical_op, next) } return result } } // Interfaces. if _, ok := types.Underlying(lhs.typ).(*types.Interface); ok { // TODO check for interface/interface comparison vs. interface/value comparison. // nil comparison if /*rhs.LLVMValue().IsConstant() &&*/ rhs.LLVMValue().IsNull() { var result llvm.Value if op == token.EQL { valueNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") typeNull := b.CreateIsNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result = b.CreateAnd(typeNull, valueNull, "") } else { valueNotNull := b.CreateIsNotNull(b.CreateExtractValue(lhs.LLVMValue(), 0, ""), "") typeNotNull := b.CreateIsNotNull(b.CreateExtractValue(lhs.LLVMValue(), 1, ""), "") result = b.CreateOr(typeNotNull, valueNotNull, "") } return c.NewLLVMValue(result, types.Bool) } // First, check that the dynamic types are identical. // FIXME provide runtime function for type identity comparison, and // value comparisons. lhsType := b.CreateExtractValue(lhs.LLVMValue(), 1, "") rhsType := b.CreateExtractValue(rhs.LLVMValue(), 1, "") diff := b.CreatePtrDiff(lhsType, rhsType, "") zero := llvm.ConstNull(diff.Type()) var result llvm.Value if op == token.EQL { typesIdentical := b.CreateICmp(llvm.IntEQ, diff, zero, "") //valuesEqual := ... //result = b.CreateAnd(typesIdentical, valuesEqual, "") result = typesIdentical } else { typesDifferent := b.CreateICmp(llvm.IntNE, diff, zero, "") //valuesUnequal := ... //result = b.CreateOr(typesDifferent, valuesUnequal, "") result = typesDifferent } return c.NewLLVMValue(result, types.Bool) } if types.Underlying(lhs.typ) == types.String { if types.Underlying(rhs.typ) == types.String { switch op { case token.ADD: return c.concatenateStrings(lhs, rhs) case token.EQL, token.LSS, token.GTR, token.LEQ, token.GEQ: return c.compareStrings(lhs, rhs, op) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } } panic("unimplemented") } // Determine whether to use integer or floating point instructions. // TODO determine the NaN rules. isfp := types.Identical(types.Underlying(lhs.typ), types.Float32) || types.Identical(types.Underlying(lhs.typ), types.Float64) switch op { case token.MUL: result = b.CreateMul(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.QUO: result = b.CreateUDiv(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.ADD: result = b.CreateAdd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.SUB: result = b.CreateSub(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, lhs.typ) case token.NEQ: if isfp { result = b.CreateFCmp(llvm.FloatONE, lhs.LLVMValue(), rhs.LLVMValue(), "") } else { result = b.CreateICmp(llvm.IntNE, lhs.LLVMValue(), rhs.LLVMValue(), "") } return lhs.compiler.NewLLVMValue(result, types.Bool) case token.EQL: result = b.CreateICmp(llvm.IntEQ, lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LSS: result = b.CreateICmp(llvm.IntULT, lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LEQ: // TODO signed/unsigned result = b.CreateICmp(llvm.IntULE, lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GTR: result = b.CreateICmp(llvm.IntUGT, lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.GEQ: result = b.CreateICmp(llvm.IntUGE, lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LAND: // FIXME change this to branch result = b.CreateAnd(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) case token.LOR: // FIXME change this to branch result = b.CreateOr(lhs.LLVMValue(), rhs.LLVMValue(), "") return lhs.compiler.NewLLVMValue(result, types.Bool) default: panic(fmt.Sprint("Unimplemented operator: ", op)) } panic("unreachable") }