Index: test/Bindings/llvm-c/echo.ll =================================================================== --- /dev/null +++ test/Bindings/llvm-c/echo.ll @@ -0,0 +1,32 @@ +; RUN: llvm-as < %s | llvm-dis > %t.orig +; RUN: llvm-as < %s | llvm-c-test --echo > %t.echo +; RUN: diff %t.orig %t.echo + +declare void @decl() + +; TODO: label, struct and metadata types +define void @types() { + %1 = alloca half + %2 = alloca float + %3 = alloca double + %4 = alloca x86_fp80 + %5 = alloca fp128 + %6 = alloca ppc_fp128 + %7 = alloca i7 + %8 = alloca void (i1)* + %9 = alloca [3 x i22] + %10 = alloca i328 addrspace(5)* + %11 = alloca <5 x i23*> + %12 = alloca x86_mmx + ret void +} + +define i32 @add(i32 %a, i32 %b) { + %1 = add i32 %a, %b + ret i32 %1 +} + +define i32 @call() { + %1 = call i32 @add(i32 23, i32 19) + ret i32 %1 +} Index: tools/llvm-c-test/CMakeLists.txt =================================================================== --- tools/llvm-c-test/CMakeLists.txt +++ tools/llvm-c-test/CMakeLists.txt @@ -37,6 +37,7 @@ add_llvm_tool(llvm-c-test calc.c disassemble.c + echo.cpp helpers.c include-all.c main.c Index: tools/llvm-c-test/calc.c =================================================================== --- tools/llvm-c-test/calc.c +++ tools/llvm-c-test/calc.c @@ -14,7 +14,6 @@ \*===----------------------------------------------------------------------===*/ #include "llvm-c-test.h" -#include "llvm-c/Core.h" #include #include #include Index: tools/llvm-c-test/echo.cpp =================================================================== --- /dev/null +++ tools/llvm-c-test/echo.cpp @@ -0,0 +1,472 @@ +//===-- echo.cpp - tool for testing libLLVM and llvm-c API ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the --echo commands in llvm-c-test. +// +// This command uses the C API to read a module and output an exact copy of it +// as output. It is used to check that the resulting module matches the input +// to validate that the C API can read and write modules properly. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c-test.h" +#include "llvm/ADT/DenseMap.h" + +#include +#include + +using namespace llvm; + +// Provide DenseMapInfo for C API opaque types. +template +struct CAPIDenseMap {}; + +// The default DenseMapInfo require to know about pointer alignement. +// Because the C API uses opaques pointer types, their alignement is unknown. +// As a result, we need to roll out our own implementation. +template +struct CAPIDenseMap { + struct CAPIDenseMapInfo { + static inline T* getEmptyKey() { + uintptr_t Val = static_cast(-1); + return reinterpret_cast(Val); + } + static inline T* getTombstoneKey() { + uintptr_t Val = static_cast(-2); + return reinterpret_cast(Val); + } + static unsigned getHashValue(const T *PtrVal) { + return hash_value(PtrVal); + } + static bool isEqual(const T *LHS, const T *RHS) { return LHS == RHS; } + }; + + typedef DenseMap Map; +}; + +typedef CAPIDenseMap::Map ValueMap; + +static LLVMTypeRef clone_type(LLVMTypeRef Src, LLVMContextRef Ctx) { + LLVMTypeKind Kind = LLVMGetTypeKind(Src); + switch (Kind) { + case LLVMVoidTypeKind: + return LLVMVoidTypeInContext(Ctx); + case LLVMHalfTypeKind: + return LLVMHalfTypeInContext(Ctx); + case LLVMFloatTypeKind: + return LLVMFloatTypeInContext(Ctx); + case LLVMDoubleTypeKind: + return LLVMDoubleTypeInContext(Ctx); + case LLVMX86_FP80TypeKind: + return LLVMX86FP80TypeInContext(Ctx); + case LLVMFP128TypeKind: + return LLVMFP128TypeInContext(Ctx); + case LLVMPPC_FP128TypeKind: + return LLVMPPCFP128TypeInContext(Ctx); + case LLVMLabelTypeKind: + return LLVMLabelTypeInContext(Ctx); + case LLVMIntegerTypeKind: + return LLVMIntTypeInContext(Ctx, LLVMGetIntTypeWidth(Src)); + case LLVMFunctionTypeKind: { + unsigned ParamCount = LLVMCountParamTypes(Src); + LLVMTypeRef* Params = nullptr; + if (ParamCount > 0) { + Params = (LLVMTypeRef*) malloc(ParamCount * sizeof(LLVMTypeRef)); + LLVMGetParamTypes(Src, Params); + for (unsigned i = 0; i < ParamCount; i++) { + Params[i] = clone_type(Params[i], Ctx); + } + } + + LLVMTypeRef FunTy = LLVMFunctionType( + clone_type(LLVMGetReturnType(Src), Ctx), + Params, ParamCount, + LLVMIsFunctionVarArg(Src) + ); + + if (ParamCount > 0) + free(Params); + + return FunTy; + } + case LLVMStructTypeKind: + break; + case LLVMArrayTypeKind: + return LLVMArrayType( + clone_type(LLVMGetElementType(Src), Ctx), + LLVMGetArrayLength(Src) + ); + case LLVMPointerTypeKind: + return LLVMPointerType( + clone_type(LLVMGetElementType(Src), Ctx), + LLVMGetPointerAddressSpace(Src) + ); + case LLVMVectorTypeKind: + return LLVMVectorType( + clone_type(LLVMGetElementType(Src), Ctx), + LLVMGetVectorSize(Src) + ); + case LLVMMetadataTypeKind: + break; + case LLVMX86_MMXTypeKind: + return LLVMX86MMXTypeInContext(Ctx); + default: + break; + } + + fprintf(stderr, "%d is not a supported typekind\n", Kind); + exit(-1); +} + +static LLVMValueRef clone_literal(LLVMValueRef Src, LLVMContextRef Ctx) { + LLVMTypeRef Ty = clone_type(LLVMTypeOf(Src), Ctx); + + LLVMTypeKind Kind = LLVMGetTypeKind(Ty); + switch (Kind) { + case LLVMIntegerTypeKind: + return LLVMConstInt(Ty, LLVMConstIntGetZExtValue(Src), false); + default: + break; + } + + fprintf(stderr, "%d is not a supported constant typekind\n", Kind); + exit(-1); +} + +static LLVMModuleRef get_module(LLVMBuilderRef Builder) { + LLVMBasicBlockRef BB = LLVMGetInsertBlock(Builder); + LLVMValueRef Fn = LLVMGetBasicBlockParent(BB); + return LLVMGetGlobalParent(Fn); +} + +static LLVMValueRef clone_value(LLVMValueRef Src, LLVMBuilderRef Builder, ValueMap &VMap) { + const char *Name = LLVMGetValueName(Src); + + // First, the value may be constant. + if (LLVMIsAConstant(Src)) { + LLVMModuleRef M = get_module(Builder); + + // Maybe it is a symbol + if (LLVMIsAGlobalValue(Src)) { + // Try function + LLVMValueRef Dst = LLVMGetNamedFunction(M, Name); + if (Dst != nullptr) + return Dst; + + // Try global variable + Dst = LLVMGetNamedGlobal(M, Name); + if (Dst != nullptr) + return Dst; + + fprintf(stderr, "Could not find @%s\n", Name); + exit(-1); + } + + // Try literal + LLVMContextRef Ctx = LLVMGetModuleContext(M); + return clone_literal(Src, Ctx); + } + + // Try undef + if (LLVMIsUndef(Src)) { + LLVMContextRef Ctx = LLVMGetModuleContext(get_module(Builder)); + LLVMTypeRef Ty = clone_type(LLVMTypeOf(Src), Ctx); + return LLVMGetUndef(Ty); + } + + // Check if this is something we already computed. + { + auto i = VMap.find(Src); + if (i != VMap.end()) + return i->second; + } + + // We tried everything, it must be an instruction + // that hasn't been generated already. + LLVMValueRef Dst = nullptr; + + LLVMOpcode Op = LLVMGetInstructionOpcode(Src); + switch(Op) { + case LLVMRet: { + int OpCount = LLVMGetNumOperands(Src); + if (OpCount == 0) + Dst = LLVMBuildRetVoid(Builder); + else + Dst = LLVMBuildRet(Builder, clone_value(LLVMGetOperand(Src, 0), + Builder, VMap)); + break; + } + case LLVMBr: + case LLVMSwitch: + case LLVMIndirectBr: + case LLVMInvoke: + case LLVMUnreachable: + break; + case LLVMAdd: { + LLVMValueRef LHS = clone_value(LLVMGetOperand(Src, 0), Builder, VMap); + LLVMValueRef RHS = clone_value(LLVMGetOperand(Src, 1), Builder, VMap); + Dst = LLVMBuildAdd(Builder, LHS, RHS, Name); + break; + } + case LLVMAlloca: { + // TODO: Add a binding for AllocaInst::getAllocatedType + LLVMTypeRef Ty = LLVMGetElementType(LLVMTypeOf(Src)); + Dst = LLVMBuildAlloca(Builder, Ty, Name); + break; + } + case LLVMCall: { + // TODO: Add binding for CallInst API + int ArgCount = LLVMGetNumOperands(Src) - 1; + SmallVector Args; + for (int i = 0; i < ArgCount; i++) { + Args.push_back(clone_value(LLVMGetOperand(Src, i), Builder, VMap)); + } + + LLVMValueRef Fn = clone_value(LLVMGetOperand(Src, ArgCount), Builder, VMap); + Dst = LLVMBuildCall(Builder, Fn, Args.data(), ArgCount, Name); + break; + } + default: + break; + } + + if (Dst == nullptr) { + fprintf(stderr, "%d is not a supported opcode\n", Op); + exit(-1); + } + + return VMap[Src] = Dst; +} + +static LLVMBasicBlockRef clone_bb(LLVMBasicBlockRef Src, LLVMValueRef Dst, ValueMap &VMap) { + // TODO: Extend C API to be able to get that name. + LLVMBasicBlockRef BB = LLVMAppendBasicBlock(Dst, ""); + + LLVMValueRef First = LLVMGetFirstInstruction(Src); + LLVMValueRef Last = LLVMGetLastInstruction(Src); + + if (First == nullptr) { + if (Last != nullptr) { + fprintf(stderr, "Has no first instruction, but last one\n"); + exit(-1); + } + + return BB; + } + + LLVMContextRef Ctx = LLVMGetModuleContext(LLVMGetGlobalParent(Dst)); + LLVMBuilderRef Builder = LLVMCreateBuilderInContext(Ctx); + LLVMPositionBuilderAtEnd(Builder, BB); + + LLVMValueRef Cur = First; + LLVMValueRef Next = nullptr; + while(true) { + clone_value(Cur, Builder, VMap); + Next = LLVMGetNextInstruction(Cur); + if (Next == nullptr) { + if (Cur != Last) { + fprintf(stderr, "Final instruction does not match Last\n"); + exit(-1); + } + + break; + } + + LLVMValueRef Prev = LLVMGetPreviousInstruction(Next); + if (Prev != Cur) { + fprintf(stderr, "Next.Previous instruction is not Current\n"); + exit(-1); + } + + Cur = Next; + } + + LLVMDisposeBuilder(Builder); + return BB; +} + +static void clone_bbs(LLVMValueRef Src, LLVMValueRef Dst, ValueMap &VMap) { + unsigned Count = LLVMCountBasicBlocks(Src); + if (Count == 0) + return; + + LLVMBasicBlockRef First = LLVMGetFirstBasicBlock(Src); + LLVMBasicBlockRef Last = LLVMGetLastBasicBlock(Src); + + LLVMBasicBlockRef Cur = First; + LLVMBasicBlockRef Next = nullptr; + while(true) { + clone_bb(Cur, Dst, VMap); + Count--; + Next = LLVMGetNextBasicBlock(Cur); + if (Next == nullptr) { + if (Cur != Last) { + fprintf(stderr, "Final basic block does not match Last\n"); + exit(-1); + } + + break; + } + + LLVMBasicBlockRef Prev = LLVMGetPreviousBasicBlock(Next); + if (Prev != Cur) { + fprintf(stderr, "Next.Previous basic bloc is not Current\n"); + exit(-1); + } + + Cur = Next; + } + + if (Count != 0) { + fprintf(stderr, "Basic block count does not match iterration\n"); + exit(-1); + } +} + +static ValueMap clone_params(LLVMValueRef Src, LLVMValueRef Dst) { + unsigned Count = LLVMCountParams(Src); + if (Count != LLVMCountParams(Dst)) { + fprintf(stderr, "Parameter count mismatch\n"); + exit(-1); + } + + ValueMap VMap; + if (Count == 0) + return VMap; + + LLVMValueRef SrcFirst = LLVMGetFirstParam(Src); + LLVMValueRef DstFirst = LLVMGetFirstParam(Dst); + LLVMValueRef SrcLast = LLVMGetLastParam(Src); + LLVMValueRef DstLast = LLVMGetLastParam(Dst); + + LLVMValueRef SrcCur = SrcFirst; + LLVMValueRef DstCur = DstFirst; + LLVMValueRef SrcNext = nullptr; + LLVMValueRef DstNext = nullptr; + while (true) { + const char *Name = LLVMGetValueName(SrcCur); + LLVMSetValueName(DstCur, Name); + + VMap[SrcCur] = DstCur; + + Count--; + SrcNext = LLVMGetNextParam(SrcCur); + DstNext = LLVMGetNextParam(DstCur); + if (SrcNext == nullptr && DstNext == nullptr) { + if (SrcCur != SrcLast) { + fprintf(stderr, "SrcLast param does not match End\n"); + exit(-1); + } + + if (DstCur != DstLast) { + fprintf(stderr, "DstLast param does not match End\n"); + exit(-1); + } + + break; + } + + if (SrcNext == nullptr) { + fprintf(stderr, "SrcNext was unexpectedly null\n"); + exit(-1); + } + + if (DstNext == nullptr) { + fprintf(stderr, "DstNext was unexpectedly null\n"); + exit(-1); + } + + LLVMValueRef SrcPrev = LLVMGetPreviousParam(SrcNext); + if (SrcPrev != SrcCur) { + fprintf(stderr, "SrcNext.Previous param is not Current\n"); + exit(-1); + } + + LLVMValueRef DstPrev = LLVMGetPreviousParam(DstNext); + if (DstPrev != DstCur) { + fprintf(stderr, "DstNext.Previous param is not Current\n"); + exit(-1); + } + + SrcCur = SrcNext; + DstCur = DstNext; + } + + if (Count != 0) { + fprintf(stderr, "Parameter count does not match iterration\n"); + exit(-1); + } + + return VMap; +} + +static LLVMValueRef clone_function(LLVMValueRef Src, LLVMModuleRef Dst) { + const char *Name = LLVMGetValueName(Src); + LLVMValueRef Fun = LLVMGetNamedFunction(Dst, Name); + if (Fun != nullptr) + return Fun; + + LLVMTypeRef SrcTy = LLVMTypeOf(Src); + LLVMTypeRef DstTy = clone_type(SrcTy, LLVMGetModuleContext(Dst)); + LLVMTypeRef FunTy = LLVMGetElementType(DstTy); + + Fun = LLVMAddFunction(Dst, Name, FunTy); + + ValueMap VMap = clone_params(Src, Fun); + clone_bbs(Src, Fun, VMap); + + return Fun; +} + +static void clone_functions(LLVMModuleRef Src, LLVMModuleRef Dst) { + LLVMValueRef Begin = LLVMGetFirstFunction(Src); + LLVMValueRef End = LLVMGetLastFunction(Src); + + LLVMValueRef Cur = Begin; + LLVMValueRef Next = nullptr; + while (true) { + clone_function(Cur, Dst); + Next = LLVMGetNextFunction(Cur); + if (Next == nullptr) { + if (Cur != End) { + fprintf(stderr, "Last function does not match End\n"); + exit(-1); + } + + break; + } + + LLVMValueRef Prev = LLVMGetPreviousFunction(Next); + if (Prev != Cur) { + fprintf(stderr, "Next.Previous function is not Current\n"); + exit(-1); + } + + Cur = Next; + } +} + +int echo(void) { + LLVMEnablePrettyStackTrace(); + + LLVMModuleRef Src = load_module(false, true); + + LLVMContextRef Ctx = LLVMContextCreate(); + LLVMModuleRef Dst = LLVMModuleCreateWithNameInContext("", Ctx); + + clone_functions(Src, Dst); + char *Str = LLVMPrintModuleToString(Dst); + fputs(Str, stdout); + + LLVMDisposeMessage(Str); + LLVMDisposeModule(Dst); + LLVMContextDispose(Ctx); + + return 0; +} Index: tools/llvm-c-test/llvm-c-test.h =================================================================== --- tools/llvm-c-test/llvm-c-test.h +++ tools/llvm-c-test/llvm-c-test.h @@ -14,11 +14,17 @@ #define LLVM_C_TEST_H #include +#include "llvm-c/Core.h" + +#ifdef __cplusplus +extern "C" { +#endif // helpers.c void tokenize_stdin(void (*cb)(char **tokens, int ntokens)); // module.c +LLVMModuleRef load_module(bool Lazy, bool New); int module_dump(bool Lazy, bool New); int module_list_functions(void); int module_list_globals(void); @@ -40,4 +46,11 @@ // targets.c int targets_list(void); +// echo.c +int echo(void); + +#ifdef __cplusplus +} +#endif /* !defined(__cplusplus) */ + #endif Index: tools/llvm-c-test/main.c =================================================================== --- tools/llvm-c-test/main.c +++ tools/llvm-c-test/main.c @@ -13,7 +13,6 @@ #include "llvm-c-test.h" #include "llvm-c/BitReader.h" -#include "llvm-c/Core.h" #include #include #include @@ -47,6 +46,9 @@ fprintf(stderr, " Read lines of triple, hex ascii machine code from stdin " "- print disassembly\n\n"); fprintf(stderr, " * --calc\n"); + fprintf(stderr, " * --echo\n"); + fprintf(stderr, + " Read object file form stdin - and print it back out\n\n"); fprintf( stderr, " Read lines of name, rpn from stdin - print generated module\n\n"); @@ -83,6 +85,8 @@ return add_named_metadata_operand(); } else if (argc == 2 && !strcmp(argv[1], "--set-metadata")) { return set_metadata(); + } else if (argc == 2 && !strcmp(argv[1], "--echo")) { + return echo(); } else { print_usage(); } Index: tools/llvm-c-test/metadata.c =================================================================== --- tools/llvm-c-test/metadata.c +++ tools/llvm-c-test/metadata.c @@ -13,7 +13,6 @@ \*===----------------------------------------------------------------------===*/ #include "llvm-c-test.h" -#include "llvm-c/Core.h" int add_named_metadata_operand(void) { LLVMModuleRef m = LLVMModuleCreateWithName("Mod"); Index: tools/llvm-c-test/module.c =================================================================== --- tools/llvm-c-test/module.c +++ tools/llvm-c-test/module.c @@ -14,7 +14,6 @@ #include "llvm-c-test.h" #include "llvm-c/BitReader.h" -#include "llvm-c/Core.h" #include #include #include @@ -26,7 +25,7 @@ exit(1); } -static LLVMModuleRef load_module(bool Lazy, bool New) { +LLVMModuleRef load_module(bool Lazy, bool New) { LLVMMemoryBufferRef MB; LLVMModuleRef M; char *msg = NULL; Index: tools/llvm-c-test/object.c =================================================================== --- tools/llvm-c-test/object.c +++ tools/llvm-c-test/object.c @@ -13,7 +13,6 @@ \*===----------------------------------------------------------------------===*/ #include "llvm-c-test.h" -#include "llvm-c/Core.h" #include "llvm-c/Object.h" #include #include