compiler: mark FFI functions as ModRef=Ref using TBAA metadata.

Fascinatingly, the fact that you can mark call instructions with
!tbaa metadata is completely undocumented. Regardless, it is true:
a !tbaa metadata for an "immutable" type will cause
AliasAnalysis::getModRefBehavior to return OnlyReadsMemory for that
call site.

Don't bother marking loads with TBAA yet since we already place
!load.invariant on them (which is as good as the TBAA "immutable"
flag) and after that we're limited by lack of !nonnull anyway.

Also, add TBAA analysis passes in our pipeline to actually engage it.
This commit is contained in:
whitequark 2016-03-28 02:52:15 +00:00
parent 418f0a5234
commit 1d8b0d46bc
2 changed files with 22 additions and 4 deletions

View File

@ -89,6 +89,10 @@ class Target:
llpassmgr = llvm.create_module_pass_manager() llpassmgr = llvm.create_module_pass_manager()
self.target_machine().target_data.add_pass(llpassmgr) self.target_machine().target_data.add_pass(llpassmgr)
# Register our alias analysis passes.
llpassmgr.add_basic_alias_analysis_pass()
llpassmgr.add_type_based_alias_analysis_pass()
# Start by cleaning up after our codegen and exposing as much # Start by cleaning up after our codegen and exposing as much
# information to LLVM as possible. # information to LLVM as possible.
llpassmgr.add_constant_merge_pass() llpassmgr.add_constant_merge_pass()

View File

@ -181,6 +181,14 @@ class LLVMIRGenerator:
self.phis = [] self.phis = []
self.debug_info_emitter = DebugInfoEmitter(self.llmodule) self.debug_info_emitter = DebugInfoEmitter(self.llmodule)
self.empty_metadata = self.llmodule.add_metadata([]) self.empty_metadata = self.llmodule.add_metadata([])
self.tbaa_tree = self.llmodule.add_metadata([
ll.MetaDataString(self.llmodule, "ARTIQ TBAA")
])
self.tbaa_noalias_call = self.llmodule.add_metadata([
ll.MetaDataString(self.llmodule, "non-aliasing function call"),
self.tbaa_tree,
ll.Constant(lli64, 1)
])
def needs_sret(self, lltyp, may_be_large=True): def needs_sret(self, lltyp, may_be_large=True):
if isinstance(lltyp, ll.VoidType): if isinstance(lltyp, ll.VoidType):
@ -1252,17 +1260,18 @@ class LLVMIRGenerator:
return llret return llret
def process_Call(self, insn): def process_Call(self, insn):
if types.is_rpc_function(insn.target_function().type): functiontyp = insn.target_function().type
if types.is_rpc_function(functiontyp):
return self._build_rpc(insn.target_function().loc, return self._build_rpc(insn.target_function().loc,
insn.target_function().type, functiontyp,
insn.arguments(), insn.arguments(),
llnormalblock=None, llunwindblock=None) llnormalblock=None, llunwindblock=None)
elif types.is_c_function(insn.target_function().type): elif types.is_c_function(functiontyp):
llfun, llargs = self._prepare_ffi_call(insn) llfun, llargs = self._prepare_ffi_call(insn)
else: else:
llfun, llargs = self._prepare_closure_call(insn) llfun, llargs = self._prepare_closure_call(insn)
if self.has_sret(insn.target_function().type): if self.has_sret(functiontyp):
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), []) llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee) llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee)
@ -1276,6 +1285,11 @@ class LLVMIRGenerator:
if insn.is_cold: if insn.is_cold:
llcall.cconv = 'coldcc' llcall.cconv = 'coldcc'
if types.is_c_function(functiontyp):
# All our global state is confined to our compilation unit,
# so by definition no FFI call can mutate it.
llcall.metadata['tbaa'] = self.tbaa_noalias_call
return llresult return llresult
def process_Invoke(self, insn): def process_Invoke(self, insn):