
Summary: Previously, `WebAssembly::mayThrow()` assumed all inputs are global addresses. But when intrinsics, such as `memcpy`, `memmove`, or `memset` are lowered to external symbols in instruction selection and later emitted as library calls. And these functions don't throw. This patch adds handling to those memory intrinsics to `mayThrow` function. But while most of libcalls don't throw, we can't guarantee all of them don't throw, so currently we conservatively return true for all other external symbols. I think a better way to solve this problem is to embed 'nounwind' info in `TargetLowering::CallLoweringInfo`, so that we can access the info from the backend. This will also enable transferring 'nounwind' properties of LLVM IR instructions. Currently we don't transfer that info and we can only access properties of callee functions, if the callees are within the module. Other targets don't need this info in the backend because they do all the processing before isel, but it will help us because that info will reduce code size increase in fixing unwind destination mismatches in CFGStackify. But for now we return false for these memory intrinsics and true for all other libcalls conservatively. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D68553 llvm-svn: 373967
82 lines
2.9 KiB
C++
82 lines
2.9 KiB
C++
//===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// This file implements several utility functions for WebAssembly.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "WebAssemblyUtilities.h"
|
|
#include "WebAssemblyMachineFunctionInfo.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineLoopInfo.h"
|
|
using namespace llvm;
|
|
|
|
const char *const WebAssembly::ClangCallTerminateFn = "__clang_call_terminate";
|
|
const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
|
|
const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
|
|
const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
|
|
const char *const WebAssembly::PersonalityWrapperFn =
|
|
"_Unwind_Wasm_CallPersonality";
|
|
|
|
/// Test whether MI is a child of some other node in an expression tree.
|
|
bool WebAssembly::isChild(const MachineInstr &MI,
|
|
const WebAssemblyFunctionInfo &MFI) {
|
|
if (MI.getNumOperands() == 0)
|
|
return false;
|
|
const MachineOperand &MO = MI.getOperand(0);
|
|
if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
|
|
return false;
|
|
Register Reg = MO.getReg();
|
|
return Register::isVirtualRegister(Reg) && MFI.isVRegStackified(Reg);
|
|
}
|
|
|
|
bool WebAssembly::mayThrow(const MachineInstr &MI) {
|
|
switch (MI.getOpcode()) {
|
|
case WebAssembly::THROW:
|
|
case WebAssembly::THROW_S:
|
|
case WebAssembly::RETHROW:
|
|
case WebAssembly::RETHROW_S:
|
|
return true;
|
|
}
|
|
if (isCallIndirect(MI.getOpcode()))
|
|
return true;
|
|
if (!MI.isCall())
|
|
return false;
|
|
|
|
const MachineOperand &MO = MI.getOperand(getCalleeOpNo(MI.getOpcode()));
|
|
assert(MO.isGlobal() || MO.isSymbol());
|
|
|
|
if (MO.isSymbol()) {
|
|
// Some intrinsics are lowered to calls to external symbols, which are then
|
|
// lowered to calls to library functions. Most of libcalls don't throw, but
|
|
// we only list some of them here now.
|
|
// TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo
|
|
// instead for more accurate info.
|
|
const char *Name = MO.getSymbolName();
|
|
if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||
|
|
strcmp(Name, "memset") == 0)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
const auto *F = dyn_cast<Function>(MO.getGlobal());
|
|
if (!F)
|
|
return true;
|
|
if (F->doesNotThrow())
|
|
return false;
|
|
// These functions never throw
|
|
if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||
|
|
F->getName() == ClangCallTerminateFn || F->getName() == StdTerminateFn)
|
|
return false;
|
|
|
|
// TODO Can we exclude call instructions that are marked as 'nounwind' in the
|
|
// original LLVm IR? (Even when the callee may throw)
|
|
return true;
|
|
}
|