diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d25ce70 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + # Workflow files stored in the + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "monthly" + reviewers: + - "vmihalko" diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..da32064 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,136 @@ +--- +name: Linux CI +on: + # TODO: rename to main when we remove the binary blobs from the git history + push: + branches: [ "master", "main", "vmihalko-devel"] + pull_request: + branches: [ "master", "main", "vmihalko-devel"] + +jobs: + build: + name: Ubuntu + strategy: + fail-fast: false + matrix: + include: + # Linux with GCC + - {os: 20.04, llvm: '6.0', compiler: gcc} + - {os: 20.04, llvm: 7, compiler: gcc} + - {os: 20.04, llvm: 8, compiler: gcc} + - {os: 20.04, llvm: 9, compiler: gcc} + - {os: 20.04, llvm: 10, compiler: gcc} + - {os: 20.04, llvm: 11, compiler: gcc} + - {os: 20.04, llvm: 12, compiler: gcc} + - {os: 22.04, llvm: 13, compiler: gcc} + - {os: 22.04, llvm: 14, compiler: gcc} + - {os: 22.04, llvm: 14, compiler: gcc, type: Debug} + + # Linux with Clang + - {os: 20.04, llvm: '6.0', compiler: clang} + - {os: 20.04, llvm: 7, compiler: clang} + - {os: 20.04, llvm: 8, compiler: clang} + - {os: 20.04, llvm: 9, compiler: clang} + - {os: 20.04, llvm: 10, compiler: clang} + - {os: 20.04, llvm: 11, compiler: clang} + - {os: 20.04, llvm: 12, compiler: clang} + - {os: 22.04, llvm: 13, compiler: clang} + - {os: 22.04, llvm: 14, compiler: clang} + - {os: 22.04, llvm: 14, compiler: clang, type: Debug} + + runs-on: ubuntu-${{matrix.os}} + env: + # for colours in ninja + CLICOLOR_FORCE: 1 + + steps: + - name: Checkout llvm2c + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt update + sudo apt install cmake ninja-build clang-${{matrix.llvm}} \ + llvm-${{matrix.llvm}}-dev + + - name: Set the environment + id: env + run: | + # Set buildtype + if [[ "${{matrix.type}}" != "" ]]; then + echo "BUILD_TYPE=${{matrix.type}}" >> $GITHUB_ENV + else + echo "BUILD_TYPE=RelWithDebInfo" >> $GITHUB_ENV + fi + + # TODO: add sanitizer support to llvm2c's CMakeLists.txt + + # Build with sanitizers + CFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" + CXXFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" + LDFLAGS="-fsanitize=address,undefined" + + # Fail on UBSAN + CFLAGS="-fno-sanitize-recover=all $CFLAGS" + CXXFLAGS="-fno-sanitize-recover=all $CXXFLAGS" + + # Make UBSAN print whole stack traces + UBSAN_OPTIONS="print_stacktrace=1" + + # Fail on any compiler warning + CFLAGS="-Werror $CFLAGS" + CXXFLAGS="-Werror $CXXFLAGS" + + # Select compiler and set compiler flags + if [[ "${{matrix.compiler}}" = "clang" ]]; then + # Clang + CC="clang-${{matrix.llvm}}" + CXX="clang++-${{matrix.llvm}}" + + # Force coloured output + CFLAGS="-fcolor-diagnostics $CFLAGS" + CXXFLAGS="-fcolor-diagnostics $CXXFLAGS" + else + # GCC + CC="gcc" + CXX="g++" + + # Force coloured output + CFLAGS="-fdiagnostics-color $CFLAGS" + CXXFLAGS="-fdiagnostics-color $CXXFLAGS" + fi + + # Save the environment + echo "CC=$CC" >> $GITHUB_ENV + echo "CXX=$CXX" >> $GITHUB_ENV + echo "CFLAGS=$CFLAGS" >> $GITHUB_ENV + echo "CXXFLAGS=$CXXFLAGS" >> $GITHUB_ENV + echo "LDFLAGS=$LDFLAGS" >> $GITHUB_ENV + echo "UBSAN_OPTIONS=$UBSAN_OPTIONS" >> $GITHUB_ENV + + - name: '[Dynamic LLVM] Configure CMake project' + run: | + cmake -S. \ + -B_build \ + -GNinja \ + -DCMAKE_BUILD_TYPE:STRING="${{matrix.type}}" \ + -DLLVM_DIR:PATH="$(llvm-config-${{matrix.llvm}} --cmakedir)" + + - name: '[Dynamic LLVM] Build' + run: cmake --build _build + + - name: '[Dynamic LLVM] Run tests' + run: cmake --build _build --target check + + - name: '[Static LLVM] Re-configure CMake project' + run: | + cmake -S. \ + -B_build \ + -DLLVM_LINK_DYLIB:BOOL=OFF + cmake --build _build --target clean + + - name: '[Static LLVM] Build' + run: cmake --build _build + + - name: '[Static LLVM] Run tests' + run: cmake --build _build --target check diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..3bac5e2 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,90 @@ +--- +name: macOS CI +on: + # TODO: rename to main when we remove the binary blobs from the git history + push: + branches: [ "master", "main", "vmihalko-devel"] + pull_request: + branches: [ "master", "main", "vmihalko-devel"] + +jobs: + build: + name: 'macOS (llvm: ${{matrix.llvm}}, build: ${{matrix.build}})' + strategy: + fail-fast: false + matrix: + llvm: [14] + build: [Debug, RelWithDebInfo] + + runs-on: macos-latest + env: + # for colours in ninja + CLICOLOR_FORCE: 1 + + steps: + - name: Checkout llvm2c + uses: actions/checkout@v3 + + - name: Install dependencies + run: brew install ninja llvm@${{matrix.llvm}} + + - name: Set environment + id: env + run: | + # TODO: add sanitizer support to llvm2c's CMakeLists.txt + + # Build with sanitizers + CFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" + CXXFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" + LDFLAGS="-fsanitize=address,undefined" + + # Fail on UBSAN + CFLAGS="-fno-sanitize-recover=all $CFLAGS" + CXXFLAGS="-fno-sanitize-recover=all $CXXFLAGS" + + # Make UBSAN print whole stack traces + UBSAN_OPTIONS="print_stacktrace=1" + + # Fail on any compiler warning + CFLAGS="-Werror $CFLAGS" + CXXFLAGS="-Werror $CXXFLAGS" + + # Force coloured output + CFLAGS="-fcolor-diagnostics $CFLAGS" + CXXFLAGS="-fcolor-diagnostics $CXXFLAGS" + + # Save the environment + echo "CC=$CC" >> $GITHUB_ENV + echo "CXX=$CXX" >> $GITHUB_ENV + echo "CFLAGS=$CFLAGS" >> $GITHUB_ENV + echo "CXXFLAGS=$CXXFLAGS" >> $GITHUB_ENV + echo "LDFLAGS=$LDFLAGS" >> $GITHUB_ENV + echo "UBSAN_OPTIONS=$UBSAN_OPTIONS" >> $GITHUB_ENV + + - name: '[Dynamic LLVM] Configure CMake project' + run: | + LLVM_CONFIG="$(brew --prefix llvm@${{matrix.llvm}})/bin/llvm-config" + cmake -S. \ + -B_build \ + -GNinja \ + -DCMAKE_BUILD_TYPE:STRING=${{matrix.build}} \ + -DLLVM_DIR:PATH="$("$LLVM_CONFIG" --cmakedir)" + + - name: '[Dynamic LLVM] Build' + run: cmake --build _build + + - name: '[Dynamic LLVM] Run tests' + run: cmake --build _build --target check + + - name: '[Static LLVM] Re-configure CMake project' + run: | + cmake -S. \ + -B_build \ + -DLLVM_LINK_DYLIB:BOOL=OFF + cmake --build _build --target clean + + - name: '[Static LLVM] Build' + run: cmake --build _build + + - name: '[Static LLVM] Run tests' + run: cmake --build _build --target check diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..7057e87 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,86 @@ +--- +name: Windows CI +on: + # TODO: rename to main when we remove the binary blobs from the git history + push: + branches: [ "master", "main", "vmihalko-devel"] + pull_request: + branches: [ "master", "main", "vmihalko-devel"] + +jobs: + build: + name: 'Windows (llvm: ${{matrix.llvm}}, build: ${{matrix.build}})' + strategy: + fail-fast: false + matrix: + llvm: [13] + build: [RelWithDebInfo] + + runs-on: windows-latest + steps: + - name: Checkout llvm2c + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + # Use LLVM build from the authors of the zig language: + # https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows#option-1-use-the-windows-zig-compiler-dev-kit + curl -o llvm.tar.xz "https://ziglang.org/deps/llvm%2bclang%2blld-${{matrix.llvm}}.0.1-x86_64-windows-msvc-release-mt.tar.xz" + + # workaround for https://github.com/actions/runner-images/issues/282 + $env:Path = "C:\\Program Files\\Git\\usr\\bin;" + $env:Path + + # extract twice to deal with dangling symlinks + tar xvf .\llvm.tar.xz + tar xvf .\llvm.tar.xz + + - name: Set environment + run: | + # TODO: add sanitizer support to llvm2c's CMakeLists.txt + + # Build with sanitizers (TODO: add UBSAN when MSVC supports it) + $CFLAGS = "/fsanitize=address" + $CXXFLAGS = "/fsanitize=address" + + # Make UBSAN print whole stack traces + $UBSAN_OPTIONS = "print_stacktrace=1" + + # Fail on any compiler warning + $CFLAGS += " /WX" + $CXXFLAGS += " /WX" + + # Save the environment + "CFLAGS=$CFLAGS" >> $env:GITHUB_ENV + "CXXFLAGS=$CXXFLAGS" >> $env:GITHUB_ENV + "UBSAN_OPTIONS=$UBSAN_OPTIONS" >> $env:GITHUB_ENV + + # TODO: LLVM cannot be build as a DLL on Windows. Uncomment when it is + # possible. + # + #- name: '[Dynamic LLVM] Configure CMake project' + # run: | + # $LLVM_CONFIG = Resolve-Path 'llvm*\bin\llvm-config.exe' + # cmake -S. ` + # -B_build ` + # -DLLVM_DIR:PATH="$(& "$LLVM_CONFIG" --cmakedir)" + # + #- name: '[Dynamic LLVM] Build' + # run: cmake --build _build --config ${{matrix.build}} + # + #- name: '[Dynamic LLVM] Run tests' + # run: cmake --build _build --target check + + - name: '[Static LLVM] Re-configure CMake project' + run: | + $LLVM_CONFIG = Resolve-Path 'llvm*\bin\llvm-config.exe' + cmake -S. ` + -B_build ` + -DLLVM_LINK_DYLIB:BOOL=OFF ` + -DLLVM_DIR:PATH="$(& "$LLVM_CONFIG" --cmakedir)" + + - name: '[Static LLVM] Build' + run: cmake --build _build --config ${{matrix.build}} + + # FIXME: enable tests on Windows + #- name: '[Static LLVM] Run tests' + # run: cmake --build _build --target check diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e5cd33..11d84db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,12 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "RelWithDebInfo" # -------------------------------------------------- set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -pedantic -fno-rtti") + +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /W4 /GR-") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -Wextra -pedantic -fno-rtti") +endif() # -------------------------------------------------- # Sanitizers @@ -65,6 +70,11 @@ include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) option(LLVM_LINK_DYLIB "Link with LLVM dynamically" ON) +if(MSVC) + message(WARNING "It is not possible to link with LLVM dynamically on Windows") + set(LLVM_LINK_DYLIB OFF) +endif() + if(LLVM_LINK_DYLIB) message(STATUS "LLVM linking: dynamic") set(llvm_libs LLVM) @@ -72,8 +82,12 @@ else() message(STATUS "LLVM linking: static") # Find the libraries that correspond to the LLVM components # that we wish to use - llvm_map_components_to_libnames(llvm_libs bitwriter core irreader linker - support) + llvm_map_components_to_libnames(llvm_libs irreader) + + if(MSVC) + add_compile_options($<$:/MTd> + $<$>:/MT>) + endif() endif() # -------------------------------------------------- @@ -83,25 +97,17 @@ set(llvm2c_sources main.cpp core/Block.cpp - core/Block.h core/Func.cpp - core/Func.h core/Program.cpp - core/Program.h expr/BinaryExpr.cpp - expr/BinaryExpr.h expr/Expr.cpp - expr/Expr.h expr/UnaryExpr.cpp - expr/UnaryExpr.h parser/ProgramParser.cpp - parser/ProgramParser.h parser/SimplifyingExprVisitor.cpp parser/addSignCasts.cpp parser/arrowify.cpp - parser/cfunc.h parser/computeGlobalVarsOrder.cpp parser/constval.cpp parser/createAllocas.cpp @@ -127,16 +133,12 @@ set(llvm2c_sources parser/parseMetadataTypes.cpp parser/parseStructDeclarations.cpp parser/parseStructItems.cpp - parser/passes.h parser/prepareBitcastUnion.cpp parser/refDeref.cpp parser/toinst.cpp - parser/toinst.h type/Type.cpp - type/Type.h type/TypeHandler.cpp - type/TypeHandler.h writer/CWriter.cpp writer/ExprWriter.cpp diff --git a/parser/createExpressions.cpp b/parser/createExpressions.cpp index e82f1b2..f8a6337 100644 --- a/parser/createExpressions.cpp +++ b/parser/createExpressions.cpp @@ -57,10 +57,6 @@ Expr* parseLLVMInstruction(const llvm::Instruction& ins, Program& program); static void parseInlineASM(const llvm::Instruction& ins, Func* func, Block* block); // FIXME: Remove this when LLVM 8 is the minimal version for LLVM2C! -static inline llvm::iterator_range args_wrapper(llvm::CallInst *CI) { - return make_range(CI->arg_begin(), CI->arg_end()); -} - static inline llvm::iterator_range args_wrapper(const llvm::CallInst *CI) { return make_range(CI->arg_begin(), CI->arg_end()); } @@ -1108,7 +1104,6 @@ Expr* parseLLVMInstruction(const llvm::Instruction& ins, Program& program) { case llvm::Instruction::BitCast: case llvm::Instruction::SExt: case llvm::Instruction::ZExt: - return parseCastInstruction(ins, program); case llvm::Instruction::FPToSI: case llvm::Instruction::SIToFP: case llvm::Instruction::FPTrunc: @@ -1118,18 +1113,19 @@ Expr* parseLLVMInstruction(const llvm::Instruction& ins, Program& program) { case llvm::Instruction::PtrToInt: case llvm::Instruction::IntToPtr: case llvm::Instruction::Trunc: - { - auto *expr = parseCastInstruction(ins, program); - if (auto castExpr = llvm::dyn_cast_or_null( expr )) - castExpr->setLossy(); - return expr; - } + return parseCastInstruction(ins, program); default: llvm::outs() << ins << "\n"; assert(false && "File contains unsupported instruction!"); abort(); } } + // { + // auto *expr = parseCastInstruction(ins, program); + // if (auto castExpr = llvm::dyn_cast_or_null( expr )) + // castExpr->setLossy(); + // return expr; + // } void createExpressions(const llvm::Module* module, Program& program, bool bitcastUnions) { assert(program.isPassCompleted(PassType::CreateConstants)); diff --git a/parser/inlineBlocks.cpp b/parser/inlineBlocks.cpp index 3115f27..8a0f15d 100644 --- a/parser/inlineBlocks.cpp +++ b/parser/inlineBlocks.cpp @@ -76,7 +76,6 @@ void inlineLoopBlocks(llvm::Loop *loop, Func *fun) { if (headminator->target == latch) header->expressions.pop_back(); auto list = std::make_unique(std::move( header->expressions )); - Expr* result = list.get(); latch->addOwnership(std::move(list)); } // header and latch are the same thing diff --git a/parser/parseMetadataTypes.cpp b/parser/parseMetadataTypes.cpp index 2e33737..f5b83e2 100644 --- a/parser/parseMetadataTypes.cpp +++ b/parser/parseMetadataTypes.cpp @@ -23,7 +23,7 @@ void p(T t, Args... toPrint) { #define CHAIN(TYPE, MEMBER) ((TYPE) ? (TYPE)->MEMBER : nullptr) -Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type *anonGVName); +std::optional fixType(Program& program, const llvm::DIType *ditype, const llvm::Type *anonGVName); void vectorToString( const std::vector &vectorOfStrings, std::string &result) { result = "("; @@ -37,28 +37,39 @@ void vectorToString( const std::vector &vectorOfStrings, std::strin result += ")"; } -Type * getFnctnPtrType(Program& program, const llvm::DIDerivedType *diDtype, +std::optional getFnctnPtrType(Program& program, const llvm::DIDerivedType *diDtype, const llvm::DISubroutineType *diStype, const llvm::FunctionType *anonGVName) { - std::string rtrnType = fixType(program, (*diStype->getTypeArray().begin()), CHAIN(anonGVName, getReturnType()))->toString(); + auto t = fixType(program, (*diStype->getTypeArray().begin()), CHAIN(anonGVName, getReturnType())); + if (!t.has_value()) + return {}; + std::string rtrnType = t.value()->toString(); std::vector fnctnTypesOfArgs; for (size_t index = 0; index < diStype->getTypeArray().size() - 1; index++) { - fnctnTypesOfArgs.push_back(fixType(program, diStype->getTypeArray()[index + 1], CHAIN(anonGVName, getFunctionParamType(index)))->toString()); + auto t = fixType(program, diStype->getTypeArray()[index + 1], CHAIN(anonGVName, getFunctionParamType(index))); + if (!t.has_value()) + return {}; + fnctnTypesOfArgs.push_back(t.value()->toString()); } std::string fnctnTypesOfArgsString; vectorToString( fnctnTypesOfArgs, fnctnTypesOfArgsString); // e.g. "(int, doubl, char *)" + auto it = program.typeHandler.ditypeCache.find(diDtype); + if (it != program.typeHandler.ditypeCache.end()) { + return it->second.get(); + } + auto nthTypeDef = program.typeHandler.getTypeDefNumber(); - // p(nthTypeDef, " " , diDtype); program.typeHandler.ditypeCache[diDtype] = std::make_unique( rtrnType + "(*", "typeDef_" + std::to_string(nthTypeDef), ")" + fnctnTypesOfArgsString); program.typeHandler.sortedTypeDefs[nthTypeDef] = static_cast(program.typeHandler.ditypeCache[diDtype].get()); return program.typeHandler.ditypeCache[diDtype].get(); } -Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * anonGVName) { + +std::optional fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * anonGVName) { if (!ditype) return program.typeHandler.voidType.get(); // pointerTypes are cached: @@ -125,11 +136,14 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a auto *CI = SR->getCount().dyn_cast(); int64_t array_size = 0; if (CI) array_size = CI->getSExtValue(); - auto arrayBaseType = anonGVName->getArrayElementType(); - while( arrayBaseType->isArrayTy() ) - arrayBaseType = arrayBaseType->getArrayElementType(); - auto ptr = std::make_unique(fixType(program, diCompType->getBaseType(), arrayBaseType), array_size); + auto arrayBaseType = anonGVName && anonGVName->isArrayTy() ? CHAIN(anonGVName, getArrayElementType()) : nullptr; + while( arrayBaseType && arrayBaseType->isArrayTy() ) + arrayBaseType = arrayBaseType->getArrayElementType(); + auto t = fixType(program, diCompType->getBaseType(), arrayBaseType); + if ( !t.has_value() ) + return {}; + auto ptr = std::make_unique(t.value(), array_size); auto* innermost_array = ptr.get(); program.typeHandler.diSubranges.push_back(std::move(ptr)); @@ -158,29 +172,42 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a return outermost_array; } else if (diCompType && llvm::dwarf::DW_TAG_structure_type == diCompType->getTag() ) { - if (anonGVName && anonGVName->getStructName().empty()) + if (anonGVName && !anonGVName->isStructTy()) + anonGVName = nullptr; + // if (diCompType->getName().empty() && (!anonGVName || !anonGVName->isStructTy())) + // return {}; + + if (anonGVName && anonGVName->getStructName().empty()) // anonymous struct return program.unnamedStructs[llvm::cast(anonGVName)].get(); std::string strctName = diCompType->getName().empty() && anonGVName ? TypeHandler::getStructName(anonGVName->getStructName().str()) : "s_" + diCompType->getName().str(); + auto strct = program.getStruct( strctName ); - if ( !strct && anonGVName) + if ( !strct && anonGVName) // struct with name: diCompType->getName().str() wasn't found strct = program.getStruct( TypeHandler::getStructName(anonGVName->getStructName().str()) ); - if( !strct ) - p("Unable to find struct with name: ", strctName, "\n"), std::terminate(); + if( !strct ) { + p("Unable to find struct with name: ", strctName, "\n"); + return {}; + } + //, std::terminate(); if (strct->items.size() != diCompType->getElements().size()) { p("Struct and DIStruct has different number of attributes."); p("Error or struct with bit-fields"); - return strct; + return {}; } program.typeHandler.StructTypeDiCache[diCompType] = strct; size_t index = 0; while( index < strct->items.size()) { llvm::DIType *di_node = llvm::dyn_cast_or_null( diCompType->getElements()[index]); - strct->items[index].first = fixType(program, di_node, CHAIN(anonGVName, getStructElementType(index))); + strct->items[index].first = fixType(program, + di_node, + CHAIN(anonGVName, + getStructElementType(index))).value_or( + strct->items[index].first); if (!di_node->getName().empty()) strct->items[index].second = di_node->getName().str(); @@ -189,7 +216,12 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a } return strct; } else if (diCompType && llvm::dwarf::DW_TAG_union_type == diCompType->getTag()) { - if (anonGVName && anonGVName->getStructName().empty()) + if (anonGVName && !anonGVName->isStructTy()) + anonGVName = nullptr; + // if (diCompType->getName().empty() && (!anonGVName || !anonGVName->isStructTy())) + // return {}; + + if (anonGVName && anonGVName->getStructName().empty()) // anonymous union return program.unnamedStructs[llvm::cast(anonGVName)].get(); /* In LLVM there are no unions; there are only structs that can be cast into * whichever type the front-end want to cast the struct into. @@ -198,19 +230,26 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a std::string unionName = diCompType->getName().empty() && anonGVName ? TypeHandler::getStructName(anonGVName->getStructName().str()) : "u_" + diCompType->getName().str(); + auto onion = program.getStruct( unionName ); - if( !onion ) - p("Unable to find union with name: ", unionName, "\n"), std::terminate(); + if ( !onion && anonGVName) // union with name: diCompType->getName().str() wasn't found + onion = program.getStruct( TypeHandler::getStructName(anonGVName->getStructName().str()) ); + if( !onion ) { + p("Unable to find union with name: ", unionName, "\n"); + return {}; + } // ASSUMPTION: union has always single type (in LLVMIR) auto r = std::max_element(diCompType->getElements().begin(), diCompType->getElements().end(), [](llvm::DINode *a, llvm::DINode *b){ return llvm::dyn_cast( a )->getSizeInBits() < llvm::dyn_cast( b )->getSizeInBits();}); if (auto t = fixType(program, llvm::dyn_cast(*r), nullptr)) { - if (onion->items.front().first->getKind() == t->getKind()) - onion->items.front().first = t; + if (t.has_value() && onion->items.front().first->getKind() == t.value()->getKind()) { + onion->items.front().first = t.value(); + return onion; + } } - return onion; + return {}; } else if (diCompType && llvm::dwarf::DW_TAG_enumeration_type == diCompType->getTag()) { //TODO: llvm2c decompiles enum as a global variable // it should be easy to add a support for enum... @@ -223,8 +262,10 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a if (diDerivedType && ( diDerivedType->getTag() == llvm::dwarf::DW_TAG_const_type || diDerivedType->getTag() == llvm::dwarf::DW_TAG_restrict_type || diDerivedType->getTag() == llvm::dwarf::DW_TAG_member - || diDerivedType->getTag() == llvm::dwarf::DW_TAG_volatile_type )) + || diDerivedType->getTag() == llvm::dwarf::DW_TAG_volatile_type )) { + return fixType(program, diDerivedType->getBaseType(), anonGVName); + } // Commented because: // 1. we cannot distinguish between "* const *" and "const **" // 2. has no effect on semantics @@ -232,15 +273,24 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a // ft->isConst = true; // return ft; if( diDerivedType && (diDerivedType->getTag() == llvm::dwarf::DW_TAG_pointer_type)) { + if (anonGVName && !anonGVName->isPointerTy()) + anonGVName = nullptr; if (!diDerivedType->getBaseType()) return program.typeHandler.cachedDITypeInserter(diDerivedType, program.typeHandler.voidType.get()); if (const llvm::DISubroutineType *diStype = llvm::dyn_cast( diDerivedType->getBaseType() )) { - return getFnctnPtrType(program, diDerivedType, diStype, llvm::cast_or_null(CHAIN(anonGVName, getPointerElementType()))); + if (anonGVName && anonGVName->getPointerElementType()->isFunctionTy()) + return getFnctnPtrType(program, diDerivedType, diStype, llvm::cast_or_null(CHAIN(anonGVName, getPointerElementType()))); + if (!anonGVName) + return getFnctnPtrType(program, diDerivedType, diStype, llvm::cast_or_null(CHAIN(anonGVName, getPointerElementType()))); + return {}; } - auto *innerType = fixType(program, diDerivedType->getBaseType(), CHAIN(anonGVName, getPointerElementType())); + auto t = fixType(program, diDerivedType->getBaseType(), CHAIN(anonGVName, getPointerElementType())); + if ( !t.has_value() ) + return {}; + auto *innerType = t.value(); auto it = program.typeHandler.ditypeCache.find(ditype); if (it != program.typeHandler.ditypeCache.end()) { @@ -248,8 +298,15 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a } return program.typeHandler.cachedDITypeInserter(diDerivedType, innerType); } else if (diDerivedType && (diDerivedType->getTag() == llvm::dwarf::DW_TAG_typedef)) { + if (!diDerivedType->getBaseType()) + return program.typeHandler.cachedDITypeInserter(diDerivedType, + program.typeHandler.voidType.get()); if (const llvm::DISubroutineType *diStype = llvm::dyn_cast( diDerivedType->getBaseType() )) { + if (anonGVName && anonGVName->isFunctionTy()) + return getFnctnPtrType(program, diDerivedType, diStype, llvm::cast_or_null(anonGVName)); + if (!anonGVName) return getFnctnPtrType(program, diDerivedType, diStype, llvm::cast_or_null(anonGVName)); + return {}; } return fixType(program, diDerivedType->getBaseType(), anonGVName); } @@ -260,7 +317,7 @@ Type *fixType(Program& program, const llvm::DIType *ditype, const llvm::Type * a p("Unknown type tag:<", ditype->getTag(), "> name<", ditype->getName(), ">\n"); std::terminate(); - return nullptr; + return {}; } static void setMetadataInfo(Program& program, const llvm::CallInst* ins, Block* block) { @@ -281,12 +338,12 @@ static void setMetadataInfo(Program& program, const llvm::CallInst* ins, Block* llvm::DILocalVariable* localVar = llvm::dyn_cast_or_null(varMD); if (auto t = fixType(program, localVar->getType(), AT)) { - if (t->getKind() != variable->getType()->getKind()) { // TODO UNION != STRUCT + if (!t.has_value() || t.value()->getKind() != variable->getType()->getKind()) { // TODO UNION != STRUCT llvm::errs() << "The type of this variable:" << localVar->getName() << " specified by the user differs from the type in DIinfo.\n"; return; } - variable->setType(t); + variable->setType(t.value()); } } } @@ -303,8 +360,8 @@ void parseMetadataTypes(const llvm::Module* module, Program& program) { llvm::DIVariable *Var = GVE->getVariable(); if ( auto gv = program.getGlobalVar( &gvar ) ) { if (auto t = fixType(program, Var->getType(), gvar.getValueType())) { - if (t->getKind() == gv->expr->getType()->getKind()) { - program.getGlobalVar( &gvar )->expr->setType(t); + if (t.has_value() && t.value()->getKind() == gv->expr->getType()->getKind()) { + program.getGlobalVar( &gvar )->expr->setType(t.value()); } } else llvm::errs() << " Global Var missing, but debuginfo occured\n"; @@ -340,19 +397,19 @@ void parseMetadataTypes(const llvm::Module* module, Program& program) { (llvm::dyn_cast(argType)->getTag() == llvm::dwarf::DW_TAG_union_type));}); if (isStructInArgs) { - p("parseMetadataTypes: looks like struct is passed by value to this function: ", func->name, "\n"); + p("parseMetadataTypes: looks like struct is passed by value to this function. Nothing to do in: ", func->name, "\n"); continue; } func->returnType = fixType(program, *function.getSubprogram()->getType()->getTypeArray().begin(), function.getReturnType() - ); + ).value_or( func->returnType ); size_t indx = 0; while( indx < func->parameters.size() ){ func->parameters[indx]->setType( fixType(program, function.getSubprogram()->getType()->getTypeArray()[indx + 1], - function.getFunctionType()->getParamType(indx))); + function.getFunctionType()->getParamType(indx)).value_or( func->parameters[indx]->getType() )); // ++indx is here because the first element from a TypeArray // is the function return type. ++indx; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5ec7948..31de37c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,7 @@ if(N EQUAL 0) set(N 1) endif() -set(CTEST_OPTS -j${N} --output-on-failure --progress ${CTEST_OPTS}) +set(CTEST_OPTS -j${N} --output-on-failure --progress ${CTEST_OPTS} -C $) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} ${CTEST_OPTS} USES_TERMINAL) add_dependencies(check llvm2c) @@ -34,10 +34,7 @@ endforeach() # -------------------------------------------------- # Tests # -------------------------------------------------- -set(LLVM2C $) -configure_file(run.in run.imd @ONLY) -file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/run - INPUT ${CMAKE_CURRENT_BINARY_DIR}/run.imd) +configure_file(run.in run @ONLY) set(tests asm @@ -56,4 +53,5 @@ set(tests foreach(test ${tests}) add_test(NAME ${test} COMMAND ./run ${test}) + set_tests_properties(${test} PROPERTIES ENVIRONMENT LLVM2C=$) endforeach() diff --git a/test/run.in b/test/run.in index 9dc0d2a..302974e 100755 --- a/test/run.in +++ b/test/run.in @@ -8,7 +8,7 @@ fi LABEL="$1" FOLDER=$LABEL -if ! [[ -e @LLVM2C@ ]]; then +if ! [[ -e "$LLVM2C" ]]; then echo "llvm2c not found!" exit 1 fi @@ -37,7 +37,7 @@ for f in @CMAKE_CURRENT_SOURCE_DIR@/inputs/$FOLDER/*.c; do @OPT@ -mem2reg $TEMPDIR/temp.ll -o $TEMPDIR/temp.ll fi - @LLVM2C@ $TEMPDIR/temp.ll --o $TEMPDIR/temp.c # >> /dev/null + "$LLVM2C" $TEMPDIR/temp.ll --o $TEMPDIR/temp.c # >> /dev/null if [[ $? != 0 ]]; then echo -e "\n\t[NOK] llvm2c failed to translate $f!"