Esempio n. 1
0
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.");
			}
		}