llvm::Value* genFloatPrimitiveMethodCall(Function& function, const SEM::Type* type, const String& methodName, const SEM::FunctionType functionType, llvm::ArrayRef<SEM::Value> templateArgs, PendingResultArray args, llvm::Value* const hintResultValue) { auto& module = function.module(); auto& builder = function.getBuilder(); const auto& typeName = type->getObjectType()->name().first(); const auto methodID = module.context().getMethodID(CanonicalizeMethodName(methodName)); const auto methodOwner = methodID.isConstructor() ? nullptr : args[0].resolveWithoutBind(function); if (methodName == "__move_to") { const auto moveToPtr = args[1].resolve(function); const auto moveToPosition = args[2].resolve(function); const auto destPtr = builder.CreateInBoundsGEP(moveToPtr, moveToPosition); const auto castedDestPtr = builder.CreatePointerCast(destPtr, genPointerType(module, type)); genMoveStore(function, methodOwner, castedDestPtr, type); return ConstantGenerator(module).getVoidUndef(); } else if (methodName == "create") { return ConstantGenerator(module).getPrimitiveFloat(typeName, 0.0); } else if (methodName == "__setdead" || methodName == "__set_dead") { // Do nothing. return ConstantGenerator(module).getVoidUndef(); } else if (methodName == "__islive" || methodName == "__is_live") { return ConstantGenerator(module).getI1(true); } else if (methodName.starts_with("implicit_cast_") || methodName.starts_with("cast_")) { const auto argType = functionType.parameterTypes().front(); const auto operand = args[0].resolve(function); const auto selfType = genType(module, type); if (isFloatType(module, argType)) { if (methodName.starts_with("implicit_cast_")) { return builder.CreateFPExt(operand, selfType); } else { return builder.CreateFPTrunc(operand, selfType); } } else if (isUnsignedIntegerType(module, argType)) { return builder.CreateUIToFP(operand, selfType); } else if (isSignedIntegerType(module, argType)) { return builder.CreateSIToFP(operand, selfType); } else { llvm_unreachable("Unknown float cast source type."); } } else if (isUnaryOp(methodName)) { const auto zero = ConstantGenerator(module).getPrimitiveFloat(typeName, 0.0); if (methodName == "implicit_cast" || methodName == "cast") { return callCastMethod(function, methodOwner, type, methodName, templateArgs.front().typeRefType(), hintResultValue); } else if (methodName == "implicit_copy" || methodName == "copy" || methodName == "plus") { return methodOwner; } else if (methodName == "minus") { return builder.CreateFNeg(methodOwner); } else if (methodName == "isZero") { return builder.CreateFCmpOEQ(methodOwner, zero); } else if (methodName == "isPositive") { return builder.CreateFCmpOGT(methodOwner, zero); } else if (methodName == "isNegative") { return builder.CreateFCmpOLT(methodOwner, zero); } else if (methodName == "abs") { // Generates: (value < 0) ? -value : value. const auto lessThanZero = builder.CreateFCmpOLT(methodOwner, zero); return builder.CreateSelect(lessThanZero, builder.CreateFNeg(methodOwner), methodOwner); } else if (methodName == "sqrt") { llvm::Type* const intrinsicTypes[] = { methodOwner->getType() }; const auto sqrtIntrinsic = llvm::Intrinsic::getDeclaration(module.getLLVMModulePtr(), llvm::Intrinsic::sqrt, intrinsicTypes); llvm::Value* const sqrtArgs[] = { methodOwner }; return builder.CreateCall(sqrtIntrinsic, sqrtArgs); } else { llvm_unreachable("Unknown primitive unary op."); } } else if (isBinaryOp(methodName)) { const auto operand = args[1].resolveWithoutBind(function); if (methodName == "add") { return builder.CreateFAdd(methodOwner, operand); } else if (methodName == "subtract") { return builder.CreateFSub(methodOwner, operand); } else if (methodName == "multiply") { return builder.CreateFMul(methodOwner, operand); } else if (methodName == "divide") { return builder.CreateFDiv(methodOwner, operand); } else if (methodName == "modulo") { return builder.CreateFRem(methodOwner, operand); } else if (methodName == "equal") { return builder.CreateFCmpOEQ(methodOwner, operand); } else if (methodName == "not_equal") { return builder.CreateFCmpONE(methodOwner, operand); } else if (methodName == "less_than") { return builder.CreateFCmpOLT(methodOwner, operand); } else if (methodName == "less_than_or_equal") { return builder.CreateFCmpOLE(methodOwner, operand); } else if (methodName == "greater_than") { return builder.CreateFCmpOGT(methodOwner, operand); } else if (methodName == "greater_than_or_equal") { return builder.CreateFCmpOGE(methodOwner, operand); } else if (methodName == "compare") { const auto isLessThan = builder.CreateFCmpOLT(methodOwner, operand); const auto isGreaterThan = builder.CreateFCmpOGT(methodOwner, operand); const auto minusOne = ConstantGenerator(module).getI8(-1); const auto zero = ConstantGenerator(module).getI8(0); const auto plusOne = ConstantGenerator(module).getI8(1); return builder.CreateSelect(isLessThan, minusOne, builder.CreateSelect(isGreaterThan, plusOne, zero)); } else { llvm_unreachable("Unknown primitive binary op."); } } else { printf("%s\n", methodName.c_str()); llvm_unreachable("Unknown primitive method."); } }
llvm::Value* UnsignedIntegerPrimitive::emitMethod(IREmitter& irEmitter, const MethodID methodID, llvm::ArrayRef<AST::Value> typeTemplateArguments, llvm::ArrayRef<AST::Value> functionTemplateArguments, PendingResultArray args, llvm::Value* const resultPtr) const { auto& builder = irEmitter.builder(); auto& function = irEmitter.function(); auto& module = irEmitter.module(); const auto& constantGenerator = irEmitter.constantGenerator(); const auto& typeGenerator = irEmitter.typeGenerator(); const auto primitiveID = typeInstance_.primitiveID(); const auto methodOwner = methodID.isConstructor() ? nullptr : args[0].resolveWithoutBind(function); const bool unsafe = module.buildOptions().unsafe; const auto abiType = this->getABIType(module, module.abiTypeBuilder(), typeTemplateArguments); const size_t selfWidth = module.abi().typeInfo().getTypeAllocSize(abiType).asBytes() * 8; const auto selfType = typeGenerator.getIntType(selfWidth); const auto zero = constantGenerator.getPrimitiveInt(primitiveID, 0); const auto unit = constantGenerator.getPrimitiveInt(primitiveID, 1); switch (methodID) { case METHOD_CREATE: case METHOD_ZERO: return zero; case METHOD_UNIT: return unit; case METHOD_LEADINGONES: { const auto operand = args[0].resolve(function); if (!unsafe) { // Check that operand <= sizeof(type) * 8. const auto maxValue = constantGenerator.getSizeTValue(selfWidth); const auto exceedsMax = builder.CreateICmpUGT(operand, maxValue); const auto exceedsMaxBB = irEmitter.createBasicBlock("exceedsMax"); const auto doesNotExceedMaxBB = irEmitter.createBasicBlock("doesNotExceedMax"); irEmitter.emitCondBranch(exceedsMax, exceedsMaxBB, doesNotExceedMaxBB); irEmitter.selectBasicBlock(exceedsMaxBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(doesNotExceedMaxBB); } const bool isSigned = false; const auto maxValue = constantGenerator.getSizeTValue(selfWidth); const auto shift = builder.CreateSub(maxValue, operand); // Use a 128-bit integer type to avoid overflow. const auto one128Bit = constantGenerator.getInt(128, 1); const auto shiftCasted = builder.CreateIntCast(shift, one128Bit->getType(), isSigned); const auto shiftedValue = builder.CreateShl(one128Bit, shiftCasted); const auto trailingOnesValue = builder.CreateSub(shiftedValue, one128Bit); const auto result = builder.CreateNot(trailingOnesValue); return builder.CreateIntCast(result, selfType, isSigned); } case METHOD_TRAILINGONES: { const auto operand = args[0].resolve(function); if (!unsafe) { // Check that operand <= sizeof(type) * 8. const auto maxValue = constantGenerator.getSizeTValue(selfWidth); const auto exceedsMax = builder.CreateICmpUGT(operand, maxValue); const auto exceedsMaxBB = irEmitter.createBasicBlock("exceedsMax"); const auto doesNotExceedMaxBB = irEmitter.createBasicBlock("doesNotExceedMax"); irEmitter.emitCondBranch(exceedsMax, exceedsMaxBB, doesNotExceedMaxBB); irEmitter.selectBasicBlock(exceedsMaxBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(doesNotExceedMaxBB); } // Use a 128-bit integer type to avoid overflow. const auto one128Bit = constantGenerator.getInt(128, 1); const bool isSigned = false; const auto operandCasted = builder.CreateIntCast(operand, one128Bit->getType(), isSigned); const auto shiftedValue = builder.CreateShl(one128Bit, operandCasted); const auto result = builder.CreateSub(shiftedValue, one128Bit); return builder.CreateIntCast(result, selfType, isSigned); } case METHOD_IMPLICITCASTFROM: case METHOD_CASTFROM: { const auto argPrimitiveID = methodID.primitiveID(); const auto operand = args[0].resolve(function); if (argPrimitiveID.isFloat()) { return builder.CreateFPToUI(operand, selfType); } else if (argPrimitiveID.isInteger() || argPrimitiveID == PrimitiveUnichar) { return builder.CreateZExtOrTrunc(operand, selfType); } else { llvm_unreachable("Unknown unsigned integer cast source type."); } } case METHOD_ALIGNMASK: { return constantGenerator.getSizeTValue(module.abi().typeInfo().getTypeRequiredAlign(abiType).asBytes() - 1); } case METHOD_SIZEOF: { return constantGenerator.getSizeTValue(module.abi().typeInfo().getTypeAllocSize(abiType).asBytes()); } case METHOD_IMPLICITCAST: case METHOD_CAST: { AST::ValueArray valueArray; for (const auto& value: typeTemplateArguments) { valueArray.push_back(value.copy()); } const auto type = AST::Type::Object(&typeInstance_, std::move(valueArray)); return callCastMethod(function, methodOwner, type, methodID, functionTemplateArguments.front().typeRefType(), resultPtr); } case METHOD_IMPLICITCOPY: case METHOD_COPY: case METHOD_MOVE: return methodOwner; case METHOD_ISZERO: return irEmitter.emitI1ToBool(builder.CreateICmpEQ(methodOwner, zero)); case METHOD_SIGNEDVALUE: { return methodOwner; } case METHOD_COUNTLEADINGZEROES: { const auto bitCount = countLeadingZeroes(function, methodOwner); // Cast to size_t. const bool isSigned = false; return builder.CreateIntCast(bitCount, typeGenerator.getSizeTType(), isSigned); } case METHOD_COUNTLEADINGONES: { const auto bitCount = countLeadingOnes(function, methodOwner); // Cast to size_t. const bool isSigned = false; return builder.CreateIntCast(bitCount, typeGenerator.getSizeTType(), isSigned); } case METHOD_COUNTTRAILINGZEROES: { const auto bitCount = countTrailingZeroes(function, methodOwner); // Cast to size_t. const bool isSigned = false; return builder.CreateIntCast(bitCount, typeGenerator.getSizeTType(), isSigned); } case METHOD_COUNTTRAILINGONES: { const auto bitCount = countTrailingOnes(function, methodOwner); // Cast to size_t. const bool isSigned = false; return builder.CreateIntCast(bitCount, typeGenerator.getSizeTType(), isSigned); } case METHOD_INCREMENT: { // TODO: add safety checks! const auto methodOwnerPtr = args[0].resolve(function); const auto incrementedValue = builder.CreateAdd(methodOwner, unit); irEmitter.emitRawStore(incrementedValue, methodOwnerPtr); return constantGenerator.getVoidUndef(); } case METHOD_DECREMENT: { // TODO: add safety checks! const auto methodOwnerPtr = args[0].resolve(function); const auto decrementedValue = builder.CreateSub(methodOwner, unit); irEmitter.emitRawStore(decrementedValue, methodOwnerPtr); return constantGenerator.getVoidUndef(); } case METHOD_DESTROY: case METHOD_SETDEAD: { // Do nothing. return constantGenerator.getVoidUndef(); } case METHOD_ISLIVE: { return constantGenerator.getBool(true); } case METHOD_ADD: { const auto operand = args[1].resolveWithoutBind(function); llvm::Value* const binaryArgs[] = { methodOwner, operand }; if (unsafe) { return builder.CreateAdd(methodOwner, operand, /*name=*/"", /*hasNUW=*/true); } else { return callArithmeticNoOverflowIntrinsic(function, llvm::Intrinsic::uadd_with_overflow, binaryArgs); } } case METHOD_SUBTRACT: { const auto operand = args[1].resolveWithoutBind(function); llvm::Value* const binaryArgs[] = { methodOwner, operand }; if (unsafe) { return builder.CreateSub(methodOwner, operand, /*name=*/"", /*hasNUW=*/true); } else { return callArithmeticNoOverflowIntrinsic(function, llvm::Intrinsic::usub_with_overflow, binaryArgs); } } case METHOD_MULTIPLY: { const auto operand = args[1].resolveWithoutBind(function); if (unsafe) { return builder.CreateMul(methodOwner, operand, /*name=*/"", /*hasNUW=*/true); } else { const auto checkDivBB = irEmitter.createBasicBlock(""); const auto trapBB = irEmitter.createBasicBlock(""); const auto endBB = irEmitter.createBasicBlock(""); const auto mulResult = builder.CreateMul(methodOwner, operand); // Check if methodOwner == 0. const auto methodOwnerIsZero = builder.CreateICmpEQ(methodOwner, zero); irEmitter.emitCondBranch(methodOwnerIsZero, endBB, checkDivBB); // If methodOwner != 0, check (mulResult / methodOwner) == operand. irEmitter.selectBasicBlock(checkDivBB); const auto divResult = builder.CreateUDiv(mulResult, methodOwner); const auto divResultIsOperand = builder.CreateICmpEQ(divResult, operand); irEmitter.emitCondBranch(divResultIsOperand, endBB, trapBB); // (mulResult / methodOwner) != operand -> trap. irEmitter.selectBasicBlock(trapBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(endBB); return mulResult; } } case METHOD_DIVIDE: { const auto operand = args[1].resolveWithoutBind(function); if (!unsafe) { const auto divisorIsZero = builder.CreateICmpEQ(operand, zero); const auto isZeroBB = irEmitter.createBasicBlock("isZero"); const auto isNotZeroBB = irEmitter.createBasicBlock("isNotZero"); irEmitter.emitCondBranch(divisorIsZero, isZeroBB, isNotZeroBB); irEmitter.selectBasicBlock(isZeroBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(isNotZeroBB); } return builder.CreateUDiv(methodOwner, operand); } case METHOD_MODULO: { const auto operand = args[1].resolveWithoutBind(function); if (!unsafe) { const auto divisorIsZero = builder.CreateICmpEQ(operand, zero); const auto isZeroBB = irEmitter.createBasicBlock("isZero"); const auto isNotZeroBB = irEmitter.createBasicBlock("isNotZero"); irEmitter.emitCondBranch(divisorIsZero, isZeroBB, isNotZeroBB); irEmitter.selectBasicBlock(isZeroBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(isNotZeroBB); } return builder.CreateURem(methodOwner, operand); } case METHOD_COMPARE: { const auto operand = args[1].resolveWithoutBind(function); const auto isLessThan = builder.CreateICmpULT(methodOwner, operand); const auto isGreaterThan = builder.CreateICmpUGT(methodOwner, operand); const auto minusOneResult = constantGenerator.getI8(-1); const auto zeroResult = constantGenerator.getI8(0); const auto plusOneResult = constantGenerator.getI8(1); return builder.CreateSelect(isLessThan, minusOneResult, builder.CreateSelect(isGreaterThan, plusOneResult, zeroResult)); } case METHOD_EQUAL: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpEQ(methodOwner, operand)); } case METHOD_NOTEQUAL: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpNE(methodOwner, operand)); } case METHOD_LESSTHAN: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpULT(methodOwner, operand)); } case METHOD_LESSTHANOREQUAL: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpULE(methodOwner, operand)); } case METHOD_GREATERTHAN: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpUGT(methodOwner, operand)); } case METHOD_GREATERTHANOREQUAL: { const auto operand = args[1].resolveWithoutBind(function); return irEmitter.emitI1ToBool(builder.CreateICmpUGE(methodOwner, operand)); } case METHOD_BITWISEAND: { const auto operand = args[1].resolveWithoutBind(function); return builder.CreateAnd(methodOwner, operand); } case METHOD_BITWISEOR: { const auto operand = args[1].resolveWithoutBind(function); return builder.CreateOr(methodOwner, operand); } case METHOD_LEFTSHIFT: { const auto operand = args[1].resolveWithoutBind(function); if (!unsafe) { // Check that operand <= leading_zeroes(value). // Calculate leading zeroes, or produce sizeof(T) * 8 - 1 if value == 0 // (which prevents shifting 0 by sizeof(T) * 8). const auto leadingZeroes = countLeadingZeroesBounded(function, methodOwner); const bool isSigned = false; const auto leadingZeroesSizeT = builder.CreateIntCast(leadingZeroes, operand->getType(), isSigned); const auto exceedsLeadingZeroes = builder.CreateICmpUGT(operand, leadingZeroesSizeT); const auto exceedsLeadingZeroesBB = irEmitter.createBasicBlock("exceedsLeadingZeroes"); const auto doesNotExceedLeadingZeroesBB = irEmitter.createBasicBlock("doesNotExceedLeadingZeroes"); irEmitter.emitCondBranch(exceedsLeadingZeroes, exceedsLeadingZeroesBB, doesNotExceedLeadingZeroesBB); irEmitter.selectBasicBlock(exceedsLeadingZeroesBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(doesNotExceedLeadingZeroesBB); } const bool isSigned = false; const auto operandCasted = builder.CreateIntCast(operand, selfType, isSigned); return builder.CreateShl(methodOwner, operandCasted); } case METHOD_RIGHTSHIFT: { const auto operand = args[1].resolveWithoutBind(function); if (!unsafe) { // Check that operand < sizeof(type) * 8. const auto maxValue = constantGenerator.getSizeTValue(selfWidth - 1); const auto exceedsMax = builder.CreateICmpUGT(operand, maxValue); const auto exceedsMaxBB = irEmitter.createBasicBlock("exceedsMax"); const auto doesNotExceedMaxBB = irEmitter.createBasicBlock("doesNotExceedMax"); irEmitter.emitCondBranch(exceedsMax, exceedsMaxBB, doesNotExceedMaxBB); irEmitter.selectBasicBlock(exceedsMaxBB); callTrapIntrinsic(function); irEmitter.selectBasicBlock(doesNotExceedMaxBB); } const bool isSigned = false; const auto operandCasted = builder.CreateIntCast(operand, selfType, isSigned); return builder.CreateLShr(methodOwner, operandCasted); } case METHOD_INRANGE: { const auto leftOperand = args[1].resolve(function); const auto rightOperand = args[2].resolve(function); return irEmitter.emitI1ToBool(builder.CreateAnd( builder.CreateICmpULE(leftOperand, methodOwner), builder.CreateICmpULE(methodOwner, rightOperand) )); } default: llvm_unreachable("Unknown unsigned integer primitive method."); } }