diff --git a/CMakeLists.txt b/CMakeLists.txt index bf33d2a0..a4b55ed5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,14 @@ if (NOT DEFINED LLVM_VERSION_MAJOR) find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + message(STATUS "LLVM_INCLUDE_DIRS: ${LLVM_INCLUDE_DIRS}") include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") include(AddLLVM) - set (CMAKE_CXX_STANDARD 14) + set (CMAKE_CXX_STANDARD 17) endif() if(LLVM_VERSION_MAJOR LESS 8) diff --git a/README.md b/README.md index d8cfe44e..abe17913 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This LLVM C backend has been resurrected by Julia Computing with various improve Installation instructions ========================= -This version of the LLVM C backend works with LLVM 10.0, and has preliminary support for LLVM 11.0. +This version of the LLVM C backend works with LLVM 10.0 and 16.0, other version of LLVM may work but are untested. Step 1: Installing LLVM ======================= diff --git a/lib/Target/CBackend/CBackend.cpp b/lib/Target/CBackend/CBackend.cpp index 0b686163..0d441ca5 100644 --- a/lib/Target/CBackend/CBackend.cpp +++ b/lib/Target/CBackend/CBackend.cpp @@ -22,14 +22,19 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" #include "llvm/Support/MathExtras.h" +#if LLVM_VERSION_MAJOR >= 16 +#include "llvm/MC/TargetRegistry.h" +#else #include "llvm/Support/TargetRegistry.h" +#endif #include "TopologicalSorter.h" #include +#include #include - #include +#include // Jackson Korba 9/29/14 #ifndef DEBUG_TYPE @@ -82,7 +87,9 @@ bool IsPowerOfTwo(unsigned long x) #endif unsigned int NumberOfElements(VectorType *TheType) { -#if LLVM_VERSION_MAJOR >= 12 +#if LLVM_VERSION_MAJOR >= 16 + return TheType->getElementCount().getFixedValue(); +#elif LLVM_VERSION_MAJOR >= 12 return TheType->getElementCount().getValue(); #else return TheType->getNumElements(); @@ -253,7 +260,11 @@ raw_ostream &CWriter::printTypeString(raw_ostream &Out, Type *Ty, if (Ty->isPointerTy()) { Out << "p"; +#if LLVM_VERSION_MAJOR >= 16 + return printTypeString(Out, Ty->getNonOpaquePointerElementType(), isSigned); +#else return printTypeString(Out, Ty->getPointerElementType(), isSigned); +#endif } switch (Ty->getTypeID()) { @@ -343,15 +354,11 @@ std::string CWriter::getArrayName(ArrayType *AT) { CBEMangle(ArrayInnards.str()); } -std::string CWriter::getVectorName(VectorType *VT, bool Aligned) { +std::string CWriter::getVectorName(VectorType *VT) { std::string astr; raw_string_ostream VectorInnards(astr); // Vectors are handled like arrays cwriter_assert(!isEmptyType(VT)); - if (Aligned) { - headerUseMsAlign(); - Out << "__MSALIGN__(" << TD->getABITypeAlignment(VT) << ") "; - } printTypeName(VectorInnards, VT->getElementType(), false); return "struct l_vector_" + utostr(NumberOfElements(VT)) + '_' + CBEMangle(VectorInnards.str()); @@ -565,7 +572,11 @@ CWriter::printTypeName(raw_ostream &Out, Type *Ty, bool isSigned, } case Type::PointerTyID: { +#if LLVM_VERSION_MAJOR >= 16 + Type *ElTy = Ty->getNonOpaquePointerElementType(); +#else Type *ElTy = Ty->getPointerElementType(); +#endif ElTy = skipEmptyArrayTypes(ElTy); return printTypeName(Out, ElTy, false) << '*'; } @@ -582,7 +593,7 @@ CWriter::printTypeName(raw_ostream &Out, Type *Ty, bool isSigned, #endif { TypedefDeclTypes.insert(Ty); - return Out << getVectorName(cast(Ty), true); + return Out << getVectorName(cast(Ty)); } default: @@ -591,18 +602,6 @@ CWriter::printTypeName(raw_ostream &Out, Type *Ty, bool isSigned, } } -raw_ostream &CWriter::printTypeNameUnaligned(raw_ostream &Out, Type *Ty, - bool isSigned) { - if (VectorType *VTy = dyn_cast(Ty)) { - // MSVC doesn't handle __declspec(align) on parameters, - // but we specify it for Vector (hoping the compiler will vectorize it) - // so we need to avoid it sometimes - TypedefDeclTypes.insert(VTy); - return Out << getVectorName(VTy, false); - } - return printTypeName(Out, Ty, isSigned); -} - raw_ostream &CWriter::printStructDeclaration(raw_ostream &Out, StructType *STy) { if (STy->isPacked()) @@ -634,7 +633,11 @@ raw_ostream &CWriter::printStructDeclaration(raw_ostream &Out, raw_ostream &CWriter::printFunctionAttributes(raw_ostream &Out, AttributeList Attrs) { SmallVector AttrsToPrint; +#if LLVM_VERSION_MAJOR >= 16 + for (const auto &FnAttr : Attrs.getFnAttrs()) { +#else for (const auto &FnAttr : Attrs.getFnAttributes()) { +#endif if (FnAttr.isEnumAttribute() || FnAttr.isIntAttribute()) { switch (FnAttr.getKindAsEnum()) { case Attribute::AttrKind::AlwaysInline: @@ -671,10 +674,18 @@ raw_ostream &CWriter::printFunctionAttributes(raw_ostream &Out, break; case Attribute::AttrKind::AllocSize: { const auto AllocSize = FnAttr.getAllocSizeArgs(); +#if LLVM_VERSION_MAJOR >= 16 + if (AllocSize.second.has_value()) { +#else if (AllocSize.second.hasValue()) { +#endif AttrsToPrint.push_back( "alloc_size(" + std::to_string(AllocSize.first) + "," + +#if LLVM_VERSION_MAJOR >= 16 + std::to_string(AllocSize.second.value()) + ")"); +#else std::to_string(AllocSize.second.getValue()) + ")"); +#endif } else { AttrsToPrint.push_back("alloc_size(" + std::to_string(AllocSize.first) + ")"); @@ -754,8 +765,13 @@ bool CWriter::isStandardMain(const FunctionType *FTy) { return false; if (CType.equals("char **") && +#if LLVM_VERSION_MAJOR >= 16 + (!T->isPointerTy() || !T->getNonOpaquePointerElementType()->isPointerTy() || + !T->getNonOpaquePointerElementType()->getNonOpaquePointerElementType()->isIntegerTy(8))) +#else (!T->isPointerTy() || !T->getPointerElementType()->isPointerTy() || !T->getPointerElementType()->getPointerElementType()->isIntegerTy(8))) +#endif return false; } @@ -771,7 +787,11 @@ CWriter::printFunctionProto(raw_ostream &Out, FunctionType *FTy, AttributeList &PAL = Attrs.first; +#if LLVM_VERSION_MAJOR >= 16 + if (PAL.hasAttributeAtIndex(AttributeList::FunctionIndex, Attribute::NoReturn)) { +#else if (PAL.hasAttribute(AttributeList::FunctionIndex, Attribute::NoReturn)) { +#endif headerUseNoReturn(); Out << "__noreturn "; } @@ -781,20 +801,33 @@ CWriter::printFunctionProto(raw_ostream &Out, FunctionType *FTy, Out << MainArgs.begin()[0].first; } else { // Should this function actually return a struct by-value? +#if LLVM_VERSION_MAJOR >= 16 + isStructReturn = PAL.hasAttributeAtIndex(1, Attribute::StructRet) || + PAL.hasAttributeAtIndex(2, Attribute::StructRet); +#else isStructReturn = PAL.hasAttribute(1, Attribute::StructRet) || PAL.hasAttribute(2, Attribute::StructRet); +#endif // Get the return type for the function. Type *RetTy; if (!isStructReturn) RetTy = FTy->getReturnType(); else { // If this is a struct-return function, print the struct-return type. +#if LLVM_VERSION_MAJOR >= 16 + RetTy = cast(FTy->getParamType(0))->getNonOpaquePointerElementType(); +#else RetTy = cast(FTy->getParamType(0))->getElementType(); +#endif } printTypeName( Out, RetTy, /*isSigned=*/ +#if LLVM_VERSION_MAJOR >= 16 + PAL.hasAttributeAtIndex(AttributeList::ReturnIndex, Attribute::SExt)); +#else PAL.hasAttribute(AttributeList::ReturnIndex, Attribute::SExt)); +#endif } switch (Attrs.second) { @@ -842,19 +875,31 @@ CWriter::printFunctionProto(raw_ostream &Out, FunctionType *FTy, if (ArgTy->isMetadataTy()) continue; +#if LLVM_VERSION_MAJOR >= 16 + if (PAL.hasAttributeAtIndex(Idx, Attribute::ByVal)) { +#else if (PAL.hasAttribute(Idx, Attribute::ByVal)) { +#endif cwriter_assert(!shouldFixMain); cwriter_assert(ArgTy->isPointerTy()); +#if LLVM_VERSION_MAJOR >= 16 + ArgTy = cast(ArgTy)->getNonOpaquePointerElementType(); +#else ArgTy = cast(ArgTy)->getElementType(); +#endif } if (PrintedArg) Out << ", "; if (shouldFixMain) Out << MainArgs.begin()[Idx].first; else - printTypeNameUnaligned( + printTypeName( Out, ArgTy, +#if LLVM_VERSION_MAJOR >= 16 + /*isSigned=*/PAL.hasAttributeAtIndex(Idx, Attribute::SExt)); +#else /*isSigned=*/PAL.hasAttribute(Idx, Attribute::SExt)); +#endif PrintedArg = true; if (ArgList) { Out << ' '; @@ -896,11 +941,16 @@ raw_ostream &CWriter::printVectorDeclaration(raw_ostream &Out, VectorType *VTy) { cwriter_assert(!isEmptyType(VTy)); // Vectors are printed like arrays - Out << getVectorName(VTy, false) << " {\n "; + Out << getVectorName(VTy) << " {\n "; printTypeName(Out, VTy->getElementType()); + headerUseAligns(); Out << " vector[" << utostr(NumberOfElements(VTy)) - << "];\n} __attribute__((aligned(" << TD->getABITypeAlignment(VTy) - << ")));\n"; +#if LLVM_VERSION_MAJOR >= 16 + << "];\n} __POSTFIXALIGN__(" << TD->getABITypeAlign(VTy).value() +#else + << "];\n} __POSTFIXALIGN__(" << TD->getABITypeAlignment(VTy) +#endif + << ");\n"; return Out; } @@ -1334,7 +1384,11 @@ void CWriter::printConstant(Constant *CPV, enum OperandContext Context) { if (ConstantInt *CI = dyn_cast(CPV)) { Type *Ty = CI->getType(); +#if LLVM_VERSION_MAJOR >= 16 + unsigned ActiveBits = CI->getValue().getSignificantBits(); +#else unsigned ActiveBits = CI->getValue().getMinSignedBits(); +#endif if (Ty == Type::getInt1Ty(CPV->getContext())) { Out << (CI->getZExtValue() ? '1' : '0'); } else if (Context != ContextNormal && Ty->getPrimitiveSizeInBits() <= 64 && @@ -1413,7 +1467,11 @@ void CWriter::printConstant(Constant *CPV, enum OperandContext Context) { // We need to grab the first part of the FP # char Buffer[100]; +#if LLVM_VERSION_MAJOR >= 16 + uint64_t ll = llvm::bit_cast(V); +#else uint64_t ll = DoubleToBits(V); +#endif sprintf(Buffer, "0x%llx", static_cast(ll)); std::string Num(&Buffer[0], &Buffer[6]); @@ -1767,7 +1825,11 @@ void CWriter::writeOperand(Value *Operand, enum OperandContext Context) { // We can't directly declare a zero-sized variable in C, so // printTypeNameForAddressableValue uses a single-byte type instead. // We fix up the pointer type here. +#if LLVM_VERSION_MAJOR >= 16 + if (!isEmptyType(Operand->getType()->getNonOpaquePointerElementType())) +#else if (!isEmptyType(Operand->getType()->getPointerElementType())) +#endif Out << "(&"; else Out << "((void*)&"; @@ -1984,6 +2046,12 @@ static void defineAttributeWeak(raw_ostream &Out) { << "#else\n" << "#define __ATTRIBUTE_WEAK__\n" << "#endif\n\n"; + // For MSVC, use the `inline` specifier instead. + Out << "#ifdef _MSC_VER\n"; + Out << "#define __MSVC_INLINE__ inline\n"; + Out << "#else\n"; + Out << "#define __MSVC_INLINE__\n"; + Out << "#endif\n"; } static void defineHidden(raw_ostream &Out) { @@ -2019,11 +2087,13 @@ static void defineUnalignedLoad(raw_ostream &Out) { Out << "#endif\n\n"; } -static void defineMsAlign(raw_ostream &Out) { +static void defineAligns(raw_ostream &Out) { Out << "#ifdef _MSC_VER\n"; - Out << "#define __MSALIGN__(X) __declspec(align(X))\n"; + Out << "#define __PREFIXALIGN__(X) __declspec(align(X))\n"; + Out << "#define __POSTFIXALIGN__(X)\n"; Out << "#else\n"; - Out << "#define __MSALIGN__(X)\n"; + Out << "#define __PREFIXALIGN__(X)\n"; + Out << "#define __POSTFIXALIGN__(X) __attribute__((aligned(X)))\n"; Out << "#endif\n\n"; } @@ -2314,8 +2384,8 @@ void CWriter::generateCompilerSpecificCode(raw_ostream &Out, defineAttributeList(Out); if (headerIncUnalignedLoad()) defineUnalignedLoad(Out); - if (headerIncMsAlign()) - defineMsAlign(Out); + if (headerIncAligns()) + defineAligns(Out); if (headerIncNanInf()) defineNanInf(Out); if (headerIncInt128()) @@ -2469,7 +2539,11 @@ void CWriter::generateHeader(Module &M) { // Ignore special globals, such as debug info. if (getGlobalVariableClass(&*I)) continue; +#if LLVM_VERSION_MAJOR >= 16 + printTypeName(NullOut, I->getType()->getNonOpaquePointerElementType(), false); +#else printTypeName(NullOut, I->getType()->getElementType(), false); +#endif } printModuleTypes(Out); @@ -2499,18 +2573,28 @@ void CWriter::generateHeader(Module &M) { if (I->isThreadLocal()) Out << "__thread "; +#if LLVM_VERSION_MAJOR >= 16 + Type *ElTy = I->getType()->getNonOpaquePointerElementType(); +#else Type *ElTy = I->getType()->getElementType(); +#endif unsigned Alignment = I->getAlignment(); bool IsOveraligned = +#if LLVM_VERSION_MAJOR >= 16 + Alignment && Alignment > TD->getABITypeAlign(ElTy).value(); +#else Alignment && Alignment > TD->getABITypeAlignment(ElTy); +#endif if (IsOveraligned) { - headerUseMsAlign(); - Out << "__MSALIGN__(" << Alignment << ") "; + headerUseAligns(); + Out << "__PREFIXALIGN__(" << Alignment << ") "; } printTypeNameForAddressableValue(Out, ElTy, false); Out << ' ' << GetValueName(&*I); - if (IsOveraligned) - Out << " __attribute__((aligned(" << Alignment << ")))"; + if (IsOveraligned) { + headerUseAligns(); + Out << " __POSTFIXALIGN__(" << Alignment << ")"; + } if (I->hasExternalWeakLinkage()) { headerUseExternalWeak(); @@ -2553,6 +2637,12 @@ void CWriter::generateHeader(Module &M) { case Intrinsic::rint: case Intrinsic::sqrt: case Intrinsic::trunc: +#if LLVM_VERSION_MAJOR >= 16 + case Intrinsic::umax: + case Intrinsic::umin: + case Intrinsic::maximum: + case Intrinsic::minimum: +#endif intrinsicsToDefine.push_back(&*I); continue; } @@ -2584,6 +2674,10 @@ void CWriter::generateHeader(Module &M) { Out << "static "; if (I->hasExternalWeakLinkage()) Out << "extern "; + if (I->hasLinkOnceLinkage()) { + headerUseAttributeWeak(); + Out << "__MSVC_INLINE__ "; + } printFunctionProto(Out, &*I); printFunctionAttributes(Out, I->getAttributes()); if (I->hasWeakLinkage() || I->hasLinkOnceLinkage()) { @@ -2623,8 +2717,13 @@ void CWriter::generateHeader(Module &M) { Out << "\n/* External Alias Declarations */\n"; for (Module::alias_iterator I = M.alias_begin(), E = M.alias_end(); I != E; ++I) { +#if LLVM_VERSION_MAJOR >= 16 + cwriter_assert(!I->isDeclaration() && + !isEmptyType(I->getType()->getNonOpaquePointerElementType())); +#else cwriter_assert(!I->isDeclaration() && !isEmptyType(I->getType()->getPointerElementType())); +#endif if (I->hasLocalLinkage()) continue; // Internal Global @@ -2637,19 +2736,28 @@ void CWriter::generateHeader(Module &M) { if (I->isThreadLocal()) Out << "__thread "; +#if LLVM_VERSION_MAJOR >= 16 + Type *ElTy = I->getType()->getNonOpaquePointerElementType(); + unsigned Alignment = I->getAliaseeObject()->getAlignment(); + bool IsOveraligned = + Alignment && Alignment > TD->getABITypeAlign(ElTy).value(); +#else Type *ElTy = I->getType()->getElementType(); unsigned Alignment = I->getBaseObject()->getAlignment(); bool IsOveraligned = Alignment && Alignment > TD->getABITypeAlignment(ElTy); +#endif if (IsOveraligned) { - headerUseMsAlign(); - Out << "__MSALIGN__(" << Alignment << ") "; + headerUseAligns(); + Out << "__PREFIXALIGN__(" << Alignment << ") "; } // GetValueName would resolve the alias, which is not what we want, // so use getName directly instead (assuming that the Alias has a name...) printTypeName(Out, ElTy, false) << " *" << I->getName(); - if (IsOveraligned) - Out << " __attribute__((aligned(" << Alignment << ")))"; + if (IsOveraligned) { + headerUseAligns(); + Out << " __POSTFIXALIGN__(" << Alignment << ")"; + } if (I->hasExternalWeakLinkage()) { headerUseExternalWeak(); @@ -2680,19 +2788,19 @@ void CWriter::generateHeader(Module &M) { // return r; // } Out << "static __forceinline "; - printTypeNameUnaligned(Out, *it, false); + printTypeName(Out, *it, false); Out << " llvm_select_"; printTypeString(Out, *it, false); Out << "("; if (isa(*it)) #if LLVM_VERSION_MAJOR >= 12 - printTypeNameUnaligned( + printTypeName( Out, VectorType::get(Type::getInt1Ty((*it)->getContext()), cast(*it)->getElementCount()), false); #else - printTypeNameUnaligned( + printTypeName( Out, VectorType::get(Type::getInt1Ty((*it)->getContext()), cast(*it)->getNumElements()), @@ -2701,11 +2809,11 @@ void CWriter::generateHeader(Module &M) { else Out << "bool"; Out << " condition, "; - printTypeNameUnaligned(Out, *it, false); + printTypeName(Out, *it, false); Out << " iftrue, "; - printTypeNameUnaligned(Out, *it, false); + printTypeName(Out, *it, false); Out << " ifnot) {\n "; - printTypeNameUnaligned(Out, *it, false); + printTypeName(Out, *it, false); Out << " r;\n"; if (isa(*it)) { unsigned n, l = NumberOfElements(cast(*it)); @@ -2755,9 +2863,9 @@ void CWriter::generateHeader(Module &M) { Out << getCmpPredicateName(Pred) << "_"; printTypeString(Out, (*it).second, isSigned); Out << "("; - printTypeNameUnaligned(Out, (*it).second, isSigned); + printTypeName(Out, (*it).second, isSigned); Out << " l, "; - printTypeNameUnaligned(Out, (*it).second, isSigned); + printTypeName(Out, (*it).second, isSigned); Out << " r) {\n "; printTypeName(Out, RTy, isSigned); Out << " c;\n"; @@ -2858,7 +2966,7 @@ void CWriter::generateHeader(Module &M) { Out << "_"; printTypeString(Out, DstTy, false); Out << "("; - printTypeNameUnaligned(Out, SrcTy, SrcSigned); + printTypeName(Out, SrcTy, SrcSigned); Out << " in) {\n"; if (opcode == Instruction::BitCast) { Out << " union {\n "; @@ -2961,21 +3069,21 @@ void CWriter::generateHeader(Module &M) { Out << " llvm_neg_"; printTypeString(Out, OpTy, false); Out << "("; - printTypeNameUnaligned(Out, OpTy, isSigned); + printTypeName(Out, OpTy, isSigned); Out << " a) {\n "; } else if (opcode == BinaryNot) { Out << " llvm_not_"; printTypeString(Out, OpTy, false); Out << "("; - printTypeNameUnaligned(Out, OpTy, isSigned); + printTypeName(Out, OpTy, isSigned); Out << " a) {\n "; } else { Out << " llvm_" << Instruction::getOpcodeName(opcode) << "_"; printTypeString(Out, OpTy, false); Out << "("; - printTypeNameUnaligned(Out, OpTy, isSigned); + printTypeName(Out, OpTy, isSigned); Out << " a, "; - printTypeNameUnaligned(Out, OpTy, isSigned); + printTypeName(Out, OpTy, isSigned); Out << " b) {\n "; } @@ -3305,7 +3413,7 @@ void CWriter::generateHeader(Module &M) { Out << " /* "; else if (printed) Out << ", "; - printTypeNameUnaligned(Out, ElTy); + printTypeName(Out, ElTy); Out << " x" << i; if (isEmptyType(ElTy)) Out << " */"; @@ -3385,17 +3493,25 @@ void CWriter::declareOneGlobalVariable(GlobalVariable *I) { if (I->isThreadLocal()) Out << "__thread "; +#if LLVM_VERSION_MAJOR >= 16 + Type *ElTy = I->getType()->getNonOpaquePointerElementType(); + unsigned Alignment = I->getAlignment(); + bool IsOveraligned = Alignment && Alignment > TD->getABITypeAlign(ElTy).value(); +#else Type *ElTy = I->getType()->getElementType(); unsigned Alignment = I->getAlignment(); bool IsOveraligned = Alignment && Alignment > TD->getABITypeAlignment(ElTy); +#endif if (IsOveraligned) { - headerUseMsAlign(); - Out << "__MSALIGN__(" << Alignment << ") "; + headerUseAligns(); + Out << "__PREFIXALIGN__(" << Alignment << ") "; } printTypeNameForAddressableValue(Out, ElTy, false); Out << ' ' << GetValueName(I); - if (IsOveraligned) - Out << " __attribute__((aligned(" << Alignment << ")))"; + if (IsOveraligned) { + headerUseAligns(); + Out << " __POSTFIXALIGN__(" << Alignment << ")"; + } if (I->hasLinkOnceLinkage()) Out << " __attribute__((common))"; @@ -3563,7 +3679,11 @@ void CWriter::printModuleTypes(raw_ostream &Out) { // Handle arbitrarily deep pointer indirection Type *PP = P; while (PP->isPointerTy()) +#if LLVM_VERSION_MAJOR >= 16 + PP = PP->getNonOpaquePointerElementType(); +#else PP = PP->getPointerElementType(); +#endif if (auto *PPF = dyn_cast(PP)) FDeps.push_back(PPF); } @@ -3589,6 +3709,15 @@ void CWriter::printModuleTypes(raw_ostream &Out) { Sorter.addEdge(I, TopologicalSortMap[Dependencies[J]]); } } +#if LLVM_VERSION_MAJOR >= 16 + std::optional> TopologicalSortResult = Sorter.sort(); + if (!TopologicalSortResult.has_value()) { + errorWithMessage("Cyclic dependencies in function definitions"); + } + for (const auto I : TopologicalSortResult.value()) { + Out << FunctionTypeDefinitions[I].NameToPrint << "\n"; + } +#else Optional> TopologicalSortResult = Sorter.sort(); if (!TopologicalSortResult.hasValue()) { errorWithMessage("Cyclic dependencies in function definitions"); @@ -3596,6 +3725,7 @@ void CWriter::printModuleTypes(raw_ostream &Out) { for (const auto I : TopologicalSortResult.getValue()) { Out << FunctionTypeDefinitions[I].NameToPrint << "\n"; } +#endif // We may have collected some intrinsic prototypes to emit. // Emit them now, before the function that uses them is emitted @@ -3634,6 +3764,9 @@ void CWriter::forwardDeclareStructs(raw_ostream &Out, Type *Ty, // Ensure function types which are only directly used by struct types will // get declared. (void)getFunctionName(FT); + } else if (VectorType *VT = dyn_cast(Ty)) { + // Print vector type out. + Out << getVectorName(VT) << ";\n"; } } @@ -3745,8 +3878,13 @@ void CWriter::printFunction(Function &F) { // If this is a struct return function, handle the result with magic. if (isStructReturn) { +#if LLVM_VERSION_MAJOR >= 16 + Type *StructTy = + cast(F.arg_begin()->getType())->getNonOpaquePointerElementType(); +#else Type *StructTy = cast(F.arg_begin()->getType())->getElementType(); +#endif Out << " "; printTypeName(Out, StructTy, false) << " StructReturn; /* Struct return temporary */\n"; @@ -3761,18 +3899,26 @@ void CWriter::printFunction(Function &F) { // print local variable information for the function for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) { if (AllocaInst *AI = isDirectAlloca(&*I)) { +#if LLVM_VERSION_MAJOR >= 16 + unsigned Alignment = AI->getAlign().value(); + bool IsOveraligned = Alignment && Alignment > TD->getABITypeAlign( + AI->getAllocatedType()).value(); +#else unsigned Alignment = AI->getAlignment(); bool IsOveraligned = Alignment && Alignment > TD->getABITypeAlignment( AI->getAllocatedType()); +#endif Out << " "; if (IsOveraligned) { - headerUseMsAlign(); - Out << "__MSALIGN__(" << Alignment << ") "; + headerUseAligns(); + Out << "__PREFIXALIGN__(" << Alignment << ") "; } printTypeNameForAddressableValue(Out, AI->getAllocatedType(), false); Out << ' ' << GetValueName(AI); - if (IsOveraligned) - Out << " __attribute__((aligned(" << Alignment << ")))"; + if (IsOveraligned) { + headerUseAligns(); + Out << " __POSTFIXALIGN__(" << Alignment << ")"; + } Out << "; /* Address-exposed local */\n"; PrintedVar = true; } else if (!isEmptyType(I->getType()) && !isInlinableInst(*I)) { @@ -3858,9 +4004,17 @@ void CWriter::printBasicBlock(BasicBlock *BB) { // Output all of the instructions in the basic block... for (BasicBlock::iterator II = BB->begin(), E = --BB->end(); II != E; ++II) { DILocation *Loc = (*II).getDebugLoc(); - if (Loc != nullptr && LastAnnotatedSourceLine != Loc->getLine()) { - Out << "#line " << Loc->getLine() << " \"" << Loc->getDirectory() << "/" - << Loc->getFilename() << "\"" + if (Loc != nullptr && Loc->getLine() != 0 && LastAnnotatedSourceLine != Loc->getLine()) { + std::string Directory = Loc->getDirectory().str(); + std::replace(Directory.begin(), Directory.end(), '\\', '/'); + std::string Filename = Loc->getFilename().str(); + std::replace(Filename.begin(), Filename.end(), '\\', '/'); + + if (!Directory.empty() && Directory[Directory.size() - 1] != '/' && !Filename.empty() && Filename[0] != '/') + Directory.push_back('/'); + + Out << "#line " << Loc->getLine() << " \"" << Directory + << Filename << "\"" << "\n"; LastAnnotatedSourceLine = Loc->getLine(); } @@ -4503,7 +4657,11 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, } FunctionType *devecFunT = FunctionType::get( funT->getReturnType()->getScalarType(), +#if LLVM_VERSION_MAJOR >= 16 + ArrayRef(devecFunParams, numParams), funT->isVarArg()); +#else makeArrayRef(devecFunParams, numParams), funT->isVarArg()); +#endif printIntrinsicDefinition(devecFunT, Opcode, OpName + "_devec", Out); } @@ -4529,7 +4687,7 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, case Intrinsic::powi: break; } - printTypeNameUnaligned(Out, funT->getParamType(i), isSigned); + printTypeName(Out, funT->getParamType(i), isSigned); Out << " " << (char)('a' + i); if (i != numParams - 1) Out << ", "; @@ -4645,6 +4803,18 @@ void CWriter::printIntrinsicDefinition(FunctionType *funT, unsigned Opcode, Out << ")"; Out << ";\n"; break; + +#if LLVM_VERSION_MAJOR >= 16 + case Intrinsic::umax: + case Intrinsic::maximum: + Out << " r = a > b ? a : b;\n"; + break; + + case Intrinsic::umin: + case Intrinsic::minimum: + Out << " r = a < b ? a : b;\n"; + break; +#endif } } else { @@ -4780,6 +4950,12 @@ bool CWriter::lowerIntrinsics(Function &F) { case Intrinsic::stackprotector: case Intrinsic::dbg_value: case Intrinsic::dbg_declare: +#if LLVM_VERSION_MAJOR >= 16 + case Intrinsic::umax: + case Intrinsic::umin: + case Intrinsic::maximum: + case Intrinsic::minimum: +#endif // We directly implement these intrinsics break; @@ -4833,7 +5009,11 @@ void CWriter::visitCallInst(CallInst &I) { Value *Callee = I.getCalledOperand(); PointerType *PTy = cast(Callee->getType()); +#if LLVM_VERSION_MAJOR >= 16 + FunctionType *FTy = cast(PTy->getNonOpaquePointerElementType()); +#else FunctionType *FTy = cast(PTy->getElementType()); +#endif // If this is a call to a struct-return function, assign to the first // parameter instead of passing it to the call. @@ -4875,7 +5055,11 @@ void CWriter::visitCallInst(CallInst &I) { if (NeedsCast) { // Ok, just cast the pointer type. Out << "(("; +#if LLVM_VERSION_MAJOR >= 16 + printTypeName(Out, I.getCalledOperand()->getType()->getNonOpaquePointerElementType(), +#else printTypeName(Out, I.getCalledOperand()->getType()->getPointerElementType(), +#endif false, std::make_pair(PAL, I.getCallingConv())); Out << "*)(void*)"; } @@ -4918,13 +5102,21 @@ void CWriter::visitCallInst(CallInst &I) { if (ArgNo < NumDeclaredParams && (*AI)->getType() != FTy->getParamType(ArgNo)) { Out << '('; - printTypeNameUnaligned( + printTypeName( Out, FTy->getParamType(ArgNo), +#if LLVM_VERSION_MAJOR >= 16 + /*isSigned=*/PAL.hasAttributeAtIndex(ArgNo + 1, Attribute::SExt)); +#else /*isSigned=*/PAL.hasAttribute(ArgNo + 1, Attribute::SExt)); +#endif Out << ')'; } // Check if the argument is expected to be passed by value. +#if LLVM_VERSION_MAJOR >= 16 + if (I.getAttributes().hasAttributeAtIndex(ArgNo + 1, Attribute::ByVal)) +#else if (I.getAttributes().hasAttribute(ArgNo + 1, Attribute::ByVal)) +#endif writeOperandDeref(*AI); else writeOperand(*AI, ContextCasted); @@ -5126,6 +5318,12 @@ bool CWriter::visitBuiltinCall(CallInst &I, Intrinsic::ID ID) { case Intrinsic::sqrt: case Intrinsic::trap: case Intrinsic::trunc: +#if LLVM_VERSION_MAJOR >= 16 + case Intrinsic::umax: + case Intrinsic::umin: + case Intrinsic::maximum: + case Intrinsic::minimum: +#endif return false; // these use the normal function call emission } } @@ -5331,7 +5529,11 @@ void CWriter::visitAllocaInst(AllocaInst &I) { Out << '('; printTypeName(Out, I.getType()); Out << ") alloca(sizeof("; +#if LLVM_VERSION_MAJOR >= 16 + printTypeName(Out, I.getAllocatedType()); +#else printTypeName(Out, I.getType()->getElementType()); +#endif if (I.isArrayAllocation()) { Out << ") * ("; writeOperand(I.getArraySize(), ContextCasted); @@ -5462,8 +5664,13 @@ void CWriter::writeMemoryAccess(Value *Operand, Type *OperandType, return; } +#if LLVM_VERSION_MAJOR >= 16 + bool IsUnaligned = + Alignment && Alignment < TD->getABITypeAlign(OperandType).value(); +#else bool IsUnaligned = Alignment && Alignment < TD->getABITypeAlignment(OperandType); +#endif if (!IsUnaligned) { Out << '*'; @@ -5475,7 +5682,7 @@ void CWriter::writeMemoryAccess(Value *Operand, Type *OperandType, } else if (IsUnaligned) { headerUseUnalignedLoad(); Out << "__UNALIGNED_LOAD__("; - printTypeNameUnaligned(Out, OperandType, false); + printTypeName(Out, OperandType, false); if (IsVolatile) Out << " volatile"; Out << ", " << Alignment << ", "; @@ -5492,14 +5699,22 @@ void CWriter::visitLoadInst(LoadInst &I) { CurInstr = &I; writeMemoryAccess(I.getOperand(0), I.getType(), I.isVolatile(), +#if LLVM_VERSION_MAJOR >= 16 + I.getAlign().value()); +#else I.getAlignment()); +#endif } void CWriter::visitStoreInst(StoreInst &I) { CurInstr = &I; writeMemoryAccess(I.getPointerOperand(), I.getOperand(0)->getType(), +#if LLVM_VERSION_MAJOR >= 16 + I.isVolatile(), I.getAlign().value()); +#else I.isVolatile(), I.getAlignment()); +#endif Out << " = "; Value *Operand = I.getOperand(0); unsigned BitMask = 0; @@ -5666,7 +5881,11 @@ void CWriter::visitInsertValueInst(InsertValueInst &IVI) { for (const unsigned *b = IVI.idx_begin(), *i = b, *e = IVI.idx_end(); i != e; ++i) { Type *IndexedTy = ExtractValueInst::getIndexedType( +#if LLVM_VERSION_MAJOR >= 16 + IVI.getOperand(0)->getType(), ArrayRef(b, i)); +#else IVI.getOperand(0)->getType(), makeArrayRef(b, i)); +#endif cwriter_assert(IndexedTy); if (IndexedTy->isArrayTy()) Out << ".array[" << *i << "]"; @@ -5690,7 +5909,11 @@ void CWriter::visitExtractValueInst(ExtractValueInst &EVI) { for (const unsigned *b = EVI.idx_begin(), *i = b, *e = EVI.idx_end(); i != e; ++i) { Type *IndexedTy = ExtractValueInst::getIndexedType( +#if LLVM_VERSION_MAJOR >= 16 + EVI.getOperand(0)->getType(), ArrayRef(b, i)); +#else EVI.getOperand(0)->getType(), makeArrayRef(b, i)); +#endif if (IndexedTy->isArrayTy()) Out << ".array[" << *i << "]"; else @@ -5700,7 +5923,12 @@ void CWriter::visitExtractValueInst(ExtractValueInst &EVI) { Out << ")"; } -LLVM_ATTRIBUTE_NORETURN void CWriter::errorWithMessage(const char *message) { +#if LLVM_VERSION_MAJOR >= 16 +[[noreturn]] +#else +LLVM_ATTRIBUTE_NORETURN +#endif +void CWriter::errorWithMessage(const char *message) { #ifndef NDEBUG errs() << message; errs() << " in: "; diff --git a/lib/Target/CBackend/CBackend.h b/lib/Target/CBackend/CBackend.h index 66ffd688..efe2a81b 100644 --- a/lib/Target/CBackend/CBackend.h +++ b/lib/Target/CBackend/CBackend.h @@ -112,7 +112,7 @@ class CWriter : public FunctionPass, public InstVisitor { bool Hidden : 1; bool AttributeList : 1; bool UnalignedLoad : 1; - bool MsAlign : 1; + bool Aligns : 1; bool NanInf : 1; bool Int128 : 1; bool ThreadFence : 1; @@ -146,7 +146,7 @@ class CWriter : public FunctionPass, public InstVisitor { USED_HEADERS_FLAG(Hidden) USED_HEADERS_FLAG(AttributeList) USED_HEADERS_FLAG(UnalignedLoad) - USED_HEADERS_FLAG(MsAlign) + USED_HEADERS_FLAG(Aligns) USED_HEADERS_FLAG(NanInf) USED_HEADERS_FLAG(Int128) USED_HEADERS_FLAG(ThreadFence) @@ -216,8 +216,6 @@ class CWriter : public FunctionPass, public InstVisitor { CallingConv::C)); raw_ostream &printTypeNameForAddressableValue(raw_ostream &Out, Type *Ty, bool isSigned = false); - raw_ostream &printTypeNameUnaligned(raw_ostream &Out, Type *Ty, - bool isSigned = false); raw_ostream &printSimpleType(raw_ostream &Out, Type *Ty, bool isSigned); raw_ostream &printTypeString(raw_ostream &Out, Type *Ty, bool isSigned); @@ -227,7 +225,7 @@ class CWriter : public FunctionPass, public InstVisitor { std::make_pair(AttributeList(), CallingConv::C)); std::string getArrayName(ArrayType *AT); - std::string getVectorName(VectorType *VT, bool Aligned); + std::string getVectorName(VectorType *VT); enum OperandContext { ContextNormal, @@ -336,7 +334,12 @@ class CWriter : public FunctionPass, public InstVisitor { errorWithMessage("unsupported LLVM instruction"); } - LLVM_ATTRIBUTE_NORETURN void errorWithMessage(const char *message); +#if LLVM_VERSION_MAJOR >= 16 + [[noreturn]] +#else + LLVM_ATTRIBUTE_NORETURN +#endif + void errorWithMessage(const char *message); bool isGotoCodeNecessary(BasicBlock *From, BasicBlock *To); bool canDeclareLocalLate(Instruction &I); diff --git a/lib/Target/CBackend/CMakeLists.txt b/lib/Target/CBackend/CMakeLists.txt index eb389a87..16a9d705 100644 --- a/lib/Target/CBackend/CMakeLists.txt +++ b/lib/Target/CBackend/CMakeLists.txt @@ -1,6 +1,13 @@ +add_llvm_component_group(CBackend) + add_subdirectory(TargetInfo) -set(LLVM_LINK_COMPONENTS +add_llvm_target(CBackendCodeGen + CBackend.cpp + CTargetMachine.cpp + TopologicalSorter.cpp + + LINK_COMPONENTS Analysis CBackendInfo CodeGen @@ -11,10 +18,10 @@ set(LLVM_LINK_COMPONENTS Target SelectionDAG TransformUtils - ) -add_llvm_target(CBackendCodeGen - CBackend.cpp - CTargetMachine.cpp - TopologicalSorter.cpp + DEPENDS + intrinsics_gen + + ADD_TO_COMPONENT + CBackend ) diff --git a/lib/Target/CBackend/CTargetMachine.cpp b/lib/Target/CBackend/CTargetMachine.cpp index ec0241e9..16f7a18b 100644 --- a/lib/Target/CBackend/CTargetMachine.cpp +++ b/lib/Target/CBackend/CTargetMachine.cpp @@ -53,6 +53,11 @@ bool CTargetMachine::addPassesToEmitFile(PassManagerBase &PM, // Lower atomic operations to libcalls PM.add(createAtomicExpandPass()); +#if LLVM_VERSION_MAJOR >= 16 + // Lower vector operations into shuffle sequences + PM.add(createExpandReductionsPass()); +#endif + PM.add(new llvm_cbe::CWriter(Out)); return false; } diff --git a/lib/Target/CBackend/CTargetMachine.h b/lib/Target/CBackend/CTargetMachine.h index 6a006728..7c7e8b97 100644 --- a/lib/Target/CBackend/CTargetMachine.h +++ b/lib/Target/CBackend/CTargetMachine.h @@ -62,12 +62,25 @@ class CTargetSubtargetInfo : public TargetSubtargetInfo { class CTargetMachine : public LLVMTargetMachine { public: CTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, - const TargetOptions &Options, Optional RM, - Optional CM, CodeGenOpt::Level OL, + const TargetOptions &Options, +#if LLVM_VERSION_MAJOR >= 16 + std::optional RM, + std::optional CM, +#else + llvm:Optional RM, + llvm:Optional CM, +#endif + CodeGenOpt::Level OL, bool /*JIT*/) : LLVMTargetMachine(T, "", TT, CPU, FS, Options, +#if LLVM_VERSION_MAJOR >= 16 + RM.value_or(Reloc::Static), + CM.value_or(CodeModel::Small), +#else RM.hasValue() ? RM.getValue() : Reloc::Static, - CM.hasValue() ? CM.getValue() : CodeModel::Small, OL), + CM.hasValue() ? CM.getValue() : CodeModel::Small, +#endif + OL), #if LLVM_VERSION_MAJOR >= 12 SubtargetInfo(*this, TT, CPU,"", FS) {} #else diff --git a/lib/Target/CBackend/TargetInfo/CBackendTargetInfo.cpp b/lib/Target/CBackend/TargetInfo/CBackendTargetInfo.cpp index 9423e0ab..85b13252 100644 --- a/lib/Target/CBackend/TargetInfo/CBackendTargetInfo.cpp +++ b/lib/Target/CBackend/TargetInfo/CBackendTargetInfo.cpp @@ -9,7 +9,11 @@ #include "../CTargetMachine.h" #include "llvm/IR/Module.h" +#if LLVM_VERSION_MAJOR >= 16 +#include "llvm/MC/TargetRegistry.h" +#else #include "llvm/Support/TargetRegistry.h" +#endif using namespace llvm; Target llvm::TheCBackendTarget; diff --git a/lib/Target/CBackend/TargetInfo/CMakeLists.txt b/lib/Target/CBackend/TargetInfo/CMakeLists.txt index 199b203a..b5ff83f1 100644 --- a/lib/Target/CBackend/TargetInfo/CMakeLists.txt +++ b/lib/Target/CBackend/TargetInfo/CMakeLists.txt @@ -1,12 +1,14 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) -set(LLVM_LINK_COMPONENTS +add_llvm_component_library(LLVMCBackendInfo + CBackendTargetInfo.cpp + + LINK_COMPONENTS MC ScalarOpts Support Target - ) -add_llvm_library(LLVMCBackendInfo - CBackendTargetInfo.cpp + ADD_TO_COMPONENT + CBackend ) diff --git a/lib/Target/CBackend/TopologicalSorter.cpp b/lib/Target/CBackend/TopologicalSorter.cpp index 1cbe191f..f66ed6a4 100644 --- a/lib/Target/CBackend/TopologicalSorter.cpp +++ b/lib/Target/CBackend/TopologicalSorter.cpp @@ -26,10 +26,19 @@ TopologicalSorter::TopologicalSorter(int Size) { Result.reserve(Size); } -llvm::Optional> TopologicalSorter::sort() { +#if LLVM_VERSION_MAJOR >= 16 +std::optional> +#else +llvm::Optional> +#endif +TopologicalSorter::sort() { for (int I = 0; I < Size; ++I) { if (visit(I)) { +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else return llvm::None; +#endif } } return Result; diff --git a/lib/Target/CBackend/TopologicalSorter.h b/lib/Target/CBackend/TopologicalSorter.h index c2f0b9dd..ba14798f 100644 --- a/lib/Target/CBackend/TopologicalSorter.h +++ b/lib/Target/CBackend/TopologicalSorter.h @@ -14,7 +14,12 @@ #ifndef TOPOLOGICALSORTER_H #define TOPOLOGICALSORTER_H +#include +#if LLVM_VERSION_MAJOR >= 16 +#include +#else #include +#endif #include namespace llvm_cbe { @@ -35,7 +40,11 @@ class TopologicalSorter { explicit TopologicalSorter(int Size); void addEdge(int Start, int End); +#if LLVM_VERSION_MAJOR >= 16 + std::optional> sort(); // Returns None if there are cycles +#else llvm::Optional> sort(); // Returns None if there are cycles +#endif }; } // namespace llvm_cbe diff --git a/runtime/windows/atomics.c b/runtime/windows/atomics.c new file mode 100644 index 00000000..1d26f366 --- /dev/null +++ b/runtime/windows/atomics.c @@ -0,0 +1,158 @@ +#include +#include +#include + +#pragma warning(disable:4100) // Unused formal parameters. + +// Workaround for MSVC bug +// https://developercommunity.visualstudio.com/t/Using-atomic_load_explicit-with-an-unsig/10414053?q=atomic_load_explicit+c4244 +#pragma warning(push) +#pragma warning(disable:4244) +uint8_t __atomic_load_1(uint8_t *src, int model) { + return atomic_load_explicit((atomic_uchar *)src, model); +} + +uint16_t __atomic_load_2(uint16_t *src, int model) { + return atomic_load_explicit((atomic_ushort *)src, model); +} +#pragma warning(pop) + +uint32_t __atomic_load_4(uint32_t *src, int model) { + return atomic_load_explicit((atomic_uint *)src, model); +} + +uint64_t __atomic_load_8(uint64_t *src, int model) { + return atomic_load_explicit((atomic_ullong *)src, model); +} + +void __atomic_store_1(uint8_t *dest, uint8_t val, int model) { + atomic_store_explicit((atomic_uchar *)dest, val, model); +} + +void __atomic_store_2(uint16_t *dest, uint16_t val, int model) { + atomic_store_explicit((atomic_ushort *)dest, val, model); +} + +void __atomic_store_4(uint32_t *dest, uint32_t val, int model) { + atomic_store_explicit((atomic_uint *)dest, val, model); +} + +void __atomic_store_8(uint64_t *dest, uint64_t val, int model) { + atomic_store_explicit((atomic_ullong *)dest, val, model); +} + +uint8_t __atomic_exchange_1(uint8_t *dest, uint8_t val, int model) { + return atomic_exchange_explicit((atomic_uchar *)dest, val, model); +} + +uint16_t __atomic_exchange_2(uint16_t *dest, uint16_t val, int model) { + return atomic_exchange_explicit((atomic_ushort *)dest, val, model); +} + +uint32_t __atomic_exchange_4(uint32_t *dest, uint32_t val, int model) { + return atomic_exchange_explicit((atomic_uint *)dest, val, model); +} + +uint64_t __atomic_exchange_8(uint64_t *dest, uint64_t val, int model) { + return atomic_exchange_explicit((atomic_ullong *)dest, val, model); +} + +bool __atomic_compare_exchange_1(uint8_t *ptr, uint8_t *expected, + uint8_t desired, int success, int failure) { + return atomic_compare_exchange_strong_explicit((atomic_uchar *)ptr, expected, desired, success, failure); +} + +bool __atomic_compare_exchange_2(uint16_t *ptr, uint16_t *expected, + uint16_t desired, int success, int failure) { + return atomic_compare_exchange_strong_explicit((atomic_ushort *)ptr, expected, desired, success, failure); +} + +bool __atomic_compare_exchange_4(uint32_t *ptr, uint32_t *expected, + uint32_t desired, int success, int failure) { + return atomic_compare_exchange_strong_explicit((atomic_uint *)ptr, expected, desired, success, failure); +} + +bool __atomic_compare_exchange_8(uint64_t *ptr, uint64_t *expected, + uint64_t desired, int success, int failure) { + return atomic_compare_exchange_strong_explicit((atomic_ullong *)ptr, expected, desired, success, failure); +} + +uint8_t __atomic_fetch_add_1(uint8_t *ptr, uint8_t val, int model) { + return atomic_fetch_add_explicit((atomic_uchar *)ptr, val, model); +} + +uint16_t __atomic_fetch_add_2(uint16_t *ptr, uint16_t val, int model) { + return atomic_fetch_add_explicit((atomic_ushort *)ptr, val, model); +} + +uint32_t __atomic_fetch_add_4(uint32_t *ptr, uint32_t val, int model) { + return atomic_fetch_add_explicit((atomic_uint *)ptr, val, model); +} + +uint64_t __atomic_fetch_add_8(uint64_t *ptr, uint64_t val, int model) { + return atomic_fetch_add_explicit((atomic_ullong *)ptr, val, model); +} + +uint8_t __atomic_fetch_sub_1(uint8_t *ptr, uint8_t val, int model) { + return atomic_fetch_sub_explicit((atomic_uchar *)ptr, val, model); +} + +uint16_t __atomic_fetch_sub_2(uint16_t *ptr, uint16_t val, int model) { + return atomic_fetch_sub_explicit((atomic_ushort *)ptr, val, model); +} + +uint32_t __atomic_fetch_sub_4(uint32_t *ptr, uint32_t val, int model) { + return atomic_fetch_sub_explicit((atomic_uint *)ptr, val, model); +} + +uint64_t __atomic_fetch_sub_8(uint64_t *ptr, uint64_t val, int model) { + return atomic_fetch_sub_explicit((atomic_ullong *)ptr, val, model); +} + +uint8_t __atomic_fetch_and_1(uint8_t *ptr, uint8_t val, int model) { + return atomic_fetch_and_explicit((atomic_uchar *)ptr, val, model); +} + +uint16_t __atomic_fetch_and_2(uint16_t *ptr, uint16_t val, int model) { + return atomic_fetch_and_explicit((atomic_ushort *)ptr, val, model); +} + +uint32_t __atomic_fetch_and_4(uint32_t *ptr, uint32_t val, int model) { + return atomic_fetch_and_explicit((atomic_uint *)ptr, val, model); +} + +uint64_t __atomic_fetch_and_8(uint64_t *ptr, uint64_t val, int model) { + return atomic_fetch_and_explicit((atomic_ullong *)ptr, val, model); +} + +uint8_t __atomic_fetch_or_1(uint8_t *ptr, uint8_t val, int model) { + return atomic_fetch_or_explicit((atomic_uchar *)ptr, val, model); +} + +uint16_t __atomic_fetch_or_2(uint16_t *ptr, uint16_t val, int model) { + return atomic_fetch_or_explicit((atomic_ushort *)ptr, val, model); +} + +uint32_t __atomic_fetch_or_4(uint32_t *ptr, uint32_t val, int model) { + return atomic_fetch_or_explicit((atomic_uint *)ptr, val, model); +} + +uint64_t __atomic_fetch_or_8(uint64_t *ptr, uint64_t val, int model) { + return atomic_fetch_or_explicit((atomic_ullong *)ptr, val, model); +} + +uint8_t __atomic_fetch_xor_1(uint8_t *ptr, uint8_t val, int model) { + return atomic_fetch_xor_explicit((atomic_uchar *)ptr, val, model); +} + +uint16_t __atomic_fetch_xor_2(uint16_t *ptr, uint16_t val, int model) { + return atomic_fetch_xor_explicit((atomic_ushort *)ptr, val, model); +} + +uint32_t __atomic_fetch_xor_4(uint32_t *ptr, uint32_t val, int model) { + return atomic_fetch_xor_explicit((atomic_uint *)ptr, val, model); +} + +uint64_t __atomic_fetch_xor_8(uint64_t *ptr, uint64_t val, int model) { + return atomic_fetch_xor_explicit((atomic_ullong *)ptr, val, model); +} diff --git a/test/ll_tests/test_addressable_empty_types.ll b/test/ll_tests/test_addressable_empty_types.ll index ae2572c5..09e8be29 100644 --- a/test/ll_tests/test_addressable_empty_types.ll +++ b/test/ll_tests/test_addressable_empty_types.ll @@ -15,11 +15,11 @@ @constant4 = constant %veryEmptyStruct zeroinitializer @constant5 = constant %extremelyEmptyArray zeroinitializer -@global1 = global %emptyStruct zeroinitializer, align 16 -@global2 = global %emptyArray zeroinitializer, align 8 -@global3 = global %veryEmptyArray zeroinitializer, align 4 -@global4 = global %veryEmptyStruct zeroinitializer, align 2 -@global5 = global %extremelyEmptyArray zeroinitializer, align 1 +@global1 = private global %emptyStruct zeroinitializer, align 16 +@global2 = private global %emptyArray zeroinitializer, align 8 +@global3 = private global %veryEmptyArray zeroinitializer, align 4 +@global4 = private global %veryEmptyStruct zeroinitializer, align 2 +@global5 = private global %extremelyEmptyArray zeroinitializer, align 1 define i32 @main() { %alloca1 = alloca %emptyStruct, align 1 diff --git a/test/ll_tests/test_byval_struct_param.ll b/test/ll_tests/test_byval_struct_param.ll index 46275db3..70538aa0 100644 --- a/test/ll_tests/test_byval_struct_param.ll +++ b/test/ll_tests/test_byval_struct_param.ll @@ -3,7 +3,7 @@ %struct.intbox = type { i32 } ; Function Attrs: noinline nounwind optnone ssp uwtable -define void @add(%struct.intbox* noalias sret %0, %struct.intbox* byval(%struct.intbox) align 4 %1, %struct.intbox* byval(%struct.intbox) align 4 %2) #0 { +define void @add(%struct.intbox* noalias sret(%struct.intbox) %0, %struct.intbox* byval(%struct.intbox) align 4 %1, %struct.intbox* byval(%struct.intbox) align 4 %2) #0 { %4 = getelementptr inbounds %struct.intbox, %struct.intbox* %1, i32 0, i32 0 %5 = load i32, i32* %4, align 4 %6 = getelementptr inbounds %struct.intbox, %struct.intbox* %2, i32 0, i32 0 @@ -23,7 +23,7 @@ define i32 @main(i32 %0, i8** %1) #0 { %6 = getelementptr inbounds %struct.intbox, %struct.intbox* %5, i32 0, i32 0 store i32 4, i32* %6, align 4 %7 = alloca %struct.intbox, align 4 - call void @add(%struct.intbox* sret %7, %struct.intbox* byval(%struct.intbox) align 4 %3, %struct.intbox* byval(%struct.intbox) align 4 %5) + call void @add(%struct.intbox* sret(%struct.intbox) %7, %struct.intbox* byval(%struct.intbox) align 4 %3, %struct.intbox* byval(%struct.intbox) align 4 %5) %8 = getelementptr inbounds %struct.intbox, %struct.intbox* %7, i32 0, i32 0 %9 = load i32, i32* %8, align 4 ret i32 %9 diff --git a/test/test_cbe.py b/test/test_cbe.py index 728668f9..8d04d5a1 100644 --- a/test/test_cbe.py +++ b/test/test_cbe.py @@ -6,6 +6,8 @@ from subprocess import call, Popen, PIPE import pytest +USE_MSVC = os.name == 'nt' + # This configuration assumes that you are using the system LLVM. # And that you created a subdir "build" to compile this from. # In other cases, please modify the lines below. @@ -21,6 +23,7 @@ CBE_FLAGS = [ # Harder to get right than early declarations, so more value to test it. '-cbe-declare-locals-late', + '-opaque-pointers=0' ] COMMON_CFLAGS = [ @@ -38,6 +41,8 @@ '-Wno-unused-but-set-variable', '-Wno-builtin-declaration-mismatch', '-Wno-error=builtin-declaration-mismatch', + '-Wno-discarded-qualifiers', + '-Wno-packed-not-aligned', '-latomic', ] @@ -46,6 +51,21 @@ CLANGFLAGS = COMMON_CFLAGS + [ '-Wno-error=unused-variable', '-Wno-unused-variable', + '-Wno-pointer-to-int-cast', + '-Wno-unused-but-set-variable', + '-Xclang', + '-no-opaque-pointers', +] + +MSVC = 'cl' +MSVCFLAGS = [ + '/std:c17', # Use C17 standard. + '/experimental:c11atomics', # Enable C11 atomics support. + '/W4', # "Informational" warning level. + '/wd4115', # Type declared in paren: In C this places it in the global namespace, which is what we want. + '/wd4189', # Variable is initialized but never refernced. + '/wd4245', # Signed/unsigned mismatch. + '/nologo', ] # exit code used by tests to indicate success @@ -54,20 +74,23 @@ TEST_XFAIL_EXIT_CODE = 25 -def check_no_output(args): - proc = Popen(args, stdout=PIPE, stderr=PIPE) +def check_no_output(args, cwd): + proc = Popen(args, cwd=cwd, stdout=PIPE, stderr=PIPE) out, err = proc.communicate() - if out or err: + if (out and not USE_MSVC) or err or proc.returncode: out = out.decode("utf-8") err = err.decode("utf-8") msg_stream = io.StringIO() - print(f"Got unexpected output from process", file=msg_stream) + print(f"Got unexpected output or exit code from process", file=msg_stream) print(f"args: {args}", file=msg_stream) print(file=msg_stream) + print(f"exit code: {proc.returncode}", file=msg_stream) + print(file=msg_stream) + print(f"stdout:", file=msg_stream) print(out, file=msg_stream) print(file=msg_stream) @@ -78,26 +101,24 @@ def check_no_output(args): raise Exception(msg_stream.getvalue()) - assert not proc.returncode, f"process exit code {proc.returncode}" - -def _compile_c(compiler, flags, c_filename, output_filename): - check_no_output([compiler, c_filename, '-o', output_filename] + flags) +def compile_gcc(c_filename, output_filename, flags=None): + flags = flags or [] + check_no_output([GCC, c_filename, '-o', output_filename] + GCCFLAGS + flags, os.path.dirname(output_filename)) return output_filename -def compile_gcc(c_filename, output_filename, flags=None): +def compile_msvc(c_filename, output_filename, flags=None): flags = flags or [] - return _compile_c(GCC, GCCFLAGS + flags, c_filename, output_filename) + atomic_library_file = os.path.join(TEST_DIR, '..', 'runtime', 'windows', 'atomics.c') + check_no_output([MSVC, c_filename, atomic_library_file, '/Fe:' + str(output_filename)] + MSVCFLAGS + flags, os.path.dirname(output_filename)) + return output_filename def compile_clang(c_filename, output_filename, flags=None, cplusplus=False): flags = flags or [] - return _compile_c( - CLANGPP if cplusplus else CLANG, - CLANGFLAGS + flags, - c_filename, - output_filename) + check_no_output([CLANGPP if cplusplus else CLANG, c_filename, '-o', output_filename] + CLANGFLAGS + flags, os.path.dirname(output_filename)) + return output_filename def compile_to_ir(c_filename, ir_filename, flags=None, cplusplus=False): @@ -108,7 +129,7 @@ def compile_to_ir(c_filename, ir_filename, flags=None, cplusplus=False): def run_llvm_cbe(ir_filename, c_filename): - check_no_output([LLVM_CBE_PATH, ir_filename, *CBE_FLAGS, '-o', c_filename]) + check_no_output([LLVM_CBE_PATH, ir_filename, *CBE_FLAGS, '-o', c_filename], os.path.dirname(ir_filename)) return c_filename @@ -170,11 +191,15 @@ def test_consistent_return_value_c(test_filename, tmpdir, cflags): # suppress "array subscript -1 is outside array bounds" in test expected # to trigger it - if "test_char_sized_ptr_math_decr" in test_filename: + if not USE_MSVC and "test_char_sized_ptr_math_decr" in test_filename: # not += to avoid affecting subsequent calls cflags = cflags + ["-Wno-array-bounds"] - cbe_exe = compile_gcc(cbe_c, tmpdir / 'cbe.exe', flags=cflags) + if USE_MSVC: + map_flags = {'-O3': '-O2', '-O0': '-Od'} + cbe_exe = compile_msvc(cbe_c, tmpdir / 'cbe.exe', flags=[f if f not in map_flags else map_flags[f] for f in cflags]) + else: + cbe_exe = compile_gcc(cbe_c, tmpdir / 'cbe.exe', flags=cflags) cbe_retval = call([cbe_exe]) print('cbe output returned', cbe_retval) assert cbe_retval == regular_retval @@ -201,7 +226,10 @@ def test_consistent_return_value_ll(test_filename, tmpdir): assert lli_retval in [TEST_SUCCESS_EXIT_CODE, TEST_XFAIL_EXIT_CODE] cbe_c = run_llvm_cbe(test_filename, tmpdir / 'cbe.c') - cbe_exe = compile_gcc(cbe_c, tmpdir / 'cbe.exe') + if USE_MSVC: + cbe_exe = compile_msvc(cbe_c, tmpdir / 'cbe.exe') + else: + cbe_exe = compile_gcc(cbe_c, tmpdir / 'cbe.exe') cbe_retval = call([cbe_exe]) print('cbe output returned', cbe_retval) assert cbe_retval == lli_retval diff --git a/tools/llvm-cbe/llvm-cbe.cpp b/tools/llvm-cbe/llvm-cbe.cpp index 05b9d679..e7f25996 100644 --- a/tools/llvm-cbe/llvm-cbe.cpp +++ b/tools/llvm-cbe/llvm-cbe.cpp @@ -13,7 +13,11 @@ // //===----------------------------------------------------------------------===// +#include +#if LLVM_VERSION_MAJOR >= 16 +#else #include "llvm/ADT/Triple.h" +#endif #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #if LLVM_VERSION_MAJOR > 10 @@ -32,6 +36,9 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" +#if LLVM_VERSION_MAJOR >= 16 +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#endif #include "llvm/MC/SubtargetFeature.h" #include "llvm/Pass.h" #if LLVM_VERSION_MAJOR >= 10 @@ -47,7 +54,11 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" +#if LLVM_VERSION_MAJOR >= 16 +#include "llvm/MC/TargetRegistry.h" +#else #include "llvm/Support/TargetRegistry.h" +#endif #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h"