/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace llvm; extern "C" LLVMBool WAMRCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M, LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions, char **OutError); extern "C" bool aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str); extern "C" void aot_add_expand_memory_op_pass(LLVMPassManagerRef pass); extern "C" void aot_func_disable_tce(LLVMValueRef func); LLVMBool WAMRCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M, LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions, char **OutError) { LLVMMCJITCompilerOptions options; // If the user passed a larger sized options struct, then they were compiled // against a newer LLVM. Tell them that something is wrong. if (SizeOfPassedOptions > sizeof(options)) { *OutError = strdup("Refusing to use options struct that is larger than " "my own; assuming LLVM library mismatch."); return 1; } // Defend against the user having an old version of the API by ensuring that // any fields they didn't see are cleared. We must defend against fields // being set to the bitwise equivalent of zero, and assume that this means // "do the default" as if that option hadn't been available. LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); memcpy(&options, PassedOptions, SizeOfPassedOptions); TargetOptions targetOptions; targetOptions.EnableFastISel = options.EnableFastISel; std::unique_ptr Mod(unwrap(M)); if (Mod) { // Set function attribute "frame-pointer" based on // NoFramePointerElim. for (auto &F : *Mod) { auto Attrs = F.getAttributes(); StringRef Value = options.NoFramePointerElim ? "all" : "none"; Attrs = Attrs.addAttribute(F.getContext(), AttributeList::FunctionIndex, "frame-pointer", Value); F.setAttributes(Attrs); } } std::string Error; bool JIT; char *host_cpu = LLVMGetHostCPUName(); if (!host_cpu) { *OutError = NULL; return false; } std::string mcpu(host_cpu); LLVMDisposeMessage(host_cpu); EngineBuilder builder(std::move(Mod)); builder.setEngineKind(EngineKind::JIT) .setErrorStr(&Error) .setMCPU(mcpu) .setOptLevel((CodeGenOpt::Level)options.OptLevel) .setTargetOptions(targetOptions); if (Optional CM = unwrap(options.CodeModel, JIT)) builder.setCodeModel(*CM); if (options.MCJMM) builder.setMCJITMemoryManager( std::unique_ptr(unwrap(options.MCJMM))); if (ExecutionEngine *JIT = builder.create()) { *OutJIT = wrap(JIT); return 0; } *OutError = strdup(Error.c_str()); return 1; } class ExpandMemoryOpPass : public llvm::ModulePass { public: static char ID; ExpandMemoryOpPass() : ModulePass(ID) {} bool runOnModule(Module &M) override; bool expandMemIntrinsicUses(Function &F); StringRef getPassName() const override { return "Expand memory operation intrinsics"; } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); } }; char ExpandMemoryOpPass::ID = 0; bool ExpandMemoryOpPass::expandMemIntrinsicUses(Function &F) { Intrinsic::ID ID = F.getIntrinsicID(); bool Changed = false; for (auto I = F.user_begin(), E = F.user_end(); I != E;) { Instruction *Inst = cast(*I); ++I; switch (ID) { case Intrinsic::memcpy: { auto *Memcpy = cast(Inst); Function *ParentFunc = Memcpy->getParent()->getParent(); const TargetTransformInfo &TTI = getAnalysis().getTTI( *ParentFunc); expandMemCpyAsLoop(Memcpy, TTI); Changed = true; Memcpy->eraseFromParent(); break; } case Intrinsic::memmove: { auto *Memmove = cast(Inst); expandMemMoveAsLoop(Memmove); Changed = true; Memmove->eraseFromParent(); break; } case Intrinsic::memset: { auto *Memset = cast(Inst); expandMemSetAsLoop(Memset); Changed = true; Memset->eraseFromParent(); break; } default: break; } } return Changed; } bool ExpandMemoryOpPass::runOnModule(Module &M) { bool Changed = false; for (Function &F : M) { if (!F.isDeclaration()) continue; switch (F.getIntrinsicID()) { case Intrinsic::memcpy: case Intrinsic::memmove: case Intrinsic::memset: if (expandMemIntrinsicUses(F)) Changed = true; break; default: break; } } return Changed; } void aot_add_expand_memory_op_pass(LLVMPassManagerRef pass) { unwrap(pass)->add(new ExpandMemoryOpPass()); } bool aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str) { #if WASM_ENABLE_SIMD != 0 if (!arch_c_str || !cpu_c_str) { return false; } llvm::SmallVector targetAttributes; llvm::Triple targetTriple(arch_c_str, "", ""); auto targetMachine = std::unique_ptr(llvm::EngineBuilder().selectTarget( targetTriple, "", std::string(cpu_c_str), targetAttributes)); if (!targetMachine) { return false; } const llvm::Triple::ArchType targetArch = targetMachine->getTargetTriple().getArch(); const llvm::MCSubtargetInfo *subTargetInfo = targetMachine->getMCSubtargetInfo(); if (subTargetInfo == nullptr) { return false; } if (targetArch == llvm::Triple::x86_64) { return subTargetInfo->checkFeatures("+sse4.1"); } else if (targetArch == llvm::Triple::aarch64) { return subTargetInfo->checkFeatures("+neon"); } else { return false; } #else (void)arch_c_str; (void)cpu_c_str; return true; #endif /* WASM_ENABLE_SIMD */ } void aot_func_disable_tce(LLVMValueRef func) { Function *F = unwrap(func); auto Attrs = F->getAttributes(); Attrs = Attrs.addAttribute(F->getContext(), AttributeList::FunctionIndex, "disable-tail-calls", "true"); F->setAttributes(Attrs); }