Add a "colocated" flag to symbol references. (#298)
This adds a "colocated" flag to function and symbolic global variables which indicates that they are defined along with the current function, so they can use PC-relative addressing. This also changes the function decl syntax; the name now always precedes the signature, and the "function" keyword is no longer included.
This commit is contained in:
35
lib/cretonne/meta/base/predicates.py
Normal file
35
lib/cretonne/meta/base/predicates.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Cretonne predicates that consider `Function` fields.
|
||||
"""
|
||||
from cdsl.predicates import FieldPredicate
|
||||
from .formats import UnaryGlobalVar
|
||||
|
||||
try:
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from cdsl.formats import FormatField # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class IsColocatedFunc(FieldPredicate):
|
||||
"""
|
||||
An instruction predicate that checks the referenced function is colocated.
|
||||
"""
|
||||
|
||||
def __init__(self, field):
|
||||
# type: (FormatField) -> None
|
||||
super(IsColocatedFunc, self).__init__(
|
||||
field, 'is_colocated_func', ('func',))
|
||||
|
||||
|
||||
class IsColocatedData(FieldPredicate):
|
||||
"""
|
||||
An instruction predicate that checks the referenced data object is
|
||||
colocated.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# type: () -> None
|
||||
super(IsColocatedData, self).__init__(
|
||||
UnaryGlobalVar.global_var, 'is_colocated_data', ('func',))
|
||||
@@ -370,9 +370,9 @@ class TypePredicate(object):
|
||||
"""
|
||||
Return Rust code for evaluating this predicate.
|
||||
|
||||
It is assumed that the context has `dfg` and `args` variables.
|
||||
It is assumed that the context has `func` and `args` variables.
|
||||
"""
|
||||
return 'dfg.value_type(args[{}]) == {}'.format(
|
||||
return 'func.dfg.value_type(args[{}]) == {}'.format(
|
||||
self.value_arg, self.value_type.rust_name())
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ class CtrlTypePredicate(object):
|
||||
"""
|
||||
Return Rust code for evaluating this predicate.
|
||||
|
||||
It is assumed that the context has `dfg` and `inst` variables.
|
||||
It is assumed that the context has `func` and `inst` variables.
|
||||
"""
|
||||
return 'dfg.ctrl_typevar(inst) == {}'.format(
|
||||
return 'func.dfg.ctrl_typevar(inst) == {}'.format(
|
||||
self.value_type.rust_name())
|
||||
|
||||
@@ -74,7 +74,7 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def emit_instp(instp, fmt, has_dfg=False):
|
||||
def emit_instp(instp, fmt, has_func=False):
|
||||
# type: (PredNode, srcgen.Formatter, bool) -> None
|
||||
"""
|
||||
Emit code for matching an instruction predicate against an
|
||||
@@ -87,7 +87,7 @@ def emit_instp(instp, fmt, has_dfg=False):
|
||||
|
||||
# Deal with pure type check predicates which apply to any instruction.
|
||||
if iform == instruction_context:
|
||||
fmt.line('let args = inst.arguments(&dfg.value_lists);')
|
||||
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
|
||||
fmt.line(instp.rust_predicate(0))
|
||||
return
|
||||
|
||||
@@ -114,11 +114,11 @@ def emit_instp(instp, fmt, has_dfg=False):
|
||||
.format(iform.name, fields), '}'):
|
||||
if has_type_check:
|
||||
# We could implement this if we need to.
|
||||
assert has_dfg, "Recipe predicates can't check type variables."
|
||||
fmt.line('let args = inst.arguments(&dfg.value_lists);')
|
||||
elif has_dfg:
|
||||
assert has_func, "Recipe predicates can't check type variables."
|
||||
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
|
||||
elif has_func:
|
||||
# Silence dead argument warning.
|
||||
fmt.line('let _ = dfg;')
|
||||
fmt.line('let _ = func;')
|
||||
fmt.format('return {};', instp.rust_predicate(0))
|
||||
fmt.line('unreachable!();')
|
||||
|
||||
@@ -132,9 +132,9 @@ def emit_inst_predicates(instps, fmt):
|
||||
for instp, number in instps.items():
|
||||
name = 'inst_predicate_{}'.format(number)
|
||||
with fmt.indented(
|
||||
'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)'
|
||||
'fn {}(func: &ir::Function, inst: &ir::InstructionData)'
|
||||
'-> bool {{'.format(name), '}'):
|
||||
emit_instp(instp, fmt, has_dfg=True)
|
||||
emit_instp(instp, fmt, has_func=True)
|
||||
|
||||
# Generate the static table.
|
||||
with fmt.indented(
|
||||
|
||||
@@ -185,9 +185,9 @@ def unwrap_inst(iref, node, fmt):
|
||||
fmt.line('ref args,')
|
||||
fmt.line('..')
|
||||
fmt.outdented_line('} = pos.func.dfg[inst] {')
|
||||
fmt.line('let dfg = &pos.func.dfg;')
|
||||
fmt.line('let func = &pos.func;')
|
||||
if iform.has_value_list:
|
||||
fmt.line('let args = args.as_slice(&dfg.value_lists);')
|
||||
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
|
||||
elif nvops == 1:
|
||||
fmt.line('let args = [arg];')
|
||||
# Generate the values for the tuple.
|
||||
@@ -198,7 +198,7 @@ def unwrap_inst(iref, node, fmt):
|
||||
fmt.format('{},', iform.imm_fields[n].member)
|
||||
elif op.is_value():
|
||||
n = expr.inst.value_opnums.index(opnum)
|
||||
fmt.format('dfg.resolve_aliases(args[{}]),', n)
|
||||
fmt.format('func.dfg.resolve_aliases(args[{}]),', n)
|
||||
# Evaluate the instruction predicate, if any.
|
||||
instp = expr.inst_predicate_with_ctrl_typevar()
|
||||
fmt.line(instp.rust_predicate(0) if instp else 'true')
|
||||
|
||||
@@ -3,8 +3,9 @@ x86 Encodings.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.predicates import IsUnsignedInt, Not, And
|
||||
from base.predicates import IsColocatedFunc, IsColocatedData
|
||||
from base import instructions as base
|
||||
from base.formats import UnaryImm
|
||||
from base.formats import UnaryImm, FuncAddr, Call
|
||||
from .defs import X86_64, X86_32
|
||||
from . import recipes as r
|
||||
from . import settings as cfg
|
||||
@@ -292,16 +293,22 @@ enc_both(base.regspill.f64, r.fregspill32, 0xf2, 0x0f, 0x11)
|
||||
# Function addresses.
|
||||
#
|
||||
|
||||
# Non-PIC, all-ones funcaddresses.
|
||||
X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
|
||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||
X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
|
||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||
|
||||
# Non-PIC, all-zeros funcaddresses.
|
||||
X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
|
||||
isap=And(allones_funcaddrs, Not(is_pic)))
|
||||
X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
|
||||
isap=And(allones_funcaddrs, Not(is_pic)))
|
||||
|
||||
# PIC
|
||||
X86_64.enc(base.func_addr.i64, *r.pcrel_fnaddr8.rex(0x8d, w=1),
|
||||
isap=is_pic,
|
||||
instp=IsColocatedFunc(FuncAddr.func_ref))
|
||||
X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
||||
isap=is_pic)
|
||||
|
||||
@@ -309,18 +316,34 @@ X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
||||
# Global addresses.
|
||||
#
|
||||
|
||||
# Non-PIC
|
||||
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8),
|
||||
isap=Not(is_pic))
|
||||
X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1),
|
||||
isap=Not(is_pic))
|
||||
|
||||
# PIC, colocated
|
||||
X86_64.enc(base.globalsym_addr.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1),
|
||||
isap=is_pic,
|
||||
instp=IsColocatedData())
|
||||
|
||||
# PIC, non-colocated
|
||||
X86_64.enc(base.globalsym_addr.i64, *r.got_gvaddr8.rex(0x8b, w=1),
|
||||
isap=is_pic)
|
||||
|
||||
#
|
||||
# Call/return
|
||||
#
|
||||
|
||||
# 32-bit, both PIC and non-PIC.
|
||||
X86_32.enc(base.call, *r.call_id(0xe8))
|
||||
|
||||
# 64-bit, PIC, colocated and non-colocated. There is no 64-bit non-PIC, since
|
||||
# non-PIC is currently using the large model, which requires calls be lowered
|
||||
# to func_addr+call_indirect.
|
||||
X86_64.enc(base.call, *r.call_id(0xe8),
|
||||
isap=is_pic,
|
||||
instp=IsColocatedFunc(Call.func_ref))
|
||||
X86_64.enc(base.call, *r.call_plt_id(0xe8), isap=is_pic)
|
||||
|
||||
X86_32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))
|
||||
|
||||
@@ -631,6 +631,21 @@ allones_fnaddr8 = TailRecipe(
|
||||
sink.put8(!0);
|
||||
''')
|
||||
|
||||
pcrel_fnaddr8 = TailRecipe(
|
||||
'pcrel_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
|
||||
# rex2 gets passed 0 for r/m register because the upper bit of
|
||||
# r/m doesnt get decoded when in rip-relative addressing mode.
|
||||
emit='''
|
||||
PUT_OP(bits, rex2(0, out_reg0), sink);
|
||||
modrm_riprel(out_reg0, sink);
|
||||
// The addend adjusts for the difference between the end of the
|
||||
// instruction and the beginning of the immediate field.
|
||||
sink.reloc_external(Reloc::X86PCRel4,
|
||||
&func.dfg.ext_funcs[func_ref].name,
|
||||
-4);
|
||||
sink.put4(0);
|
||||
''')
|
||||
|
||||
got_fnaddr8 = TailRecipe(
|
||||
'got_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
|
||||
# rex2 gets passed 0 for r/m register because the upper bit of
|
||||
@@ -669,6 +684,20 @@ gvaddr8 = TailRecipe(
|
||||
sink.put8(0);
|
||||
''')
|
||||
|
||||
# XX+rd iq with PCRel4 globalsym relocation.
|
||||
pcrel_gvaddr8 = TailRecipe(
|
||||
'pcrel_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
|
||||
emit='''
|
||||
PUT_OP(bits, rex2(0, out_reg0), sink);
|
||||
modrm_rm(5, out_reg0, sink);
|
||||
// The addend adjusts for the difference between the end of the
|
||||
// instruction and the beginning of the immediate field.
|
||||
sink.reloc_external(Reloc::X86PCRel4,
|
||||
&func.global_vars[global_var].symbol_name(),
|
||||
-4);
|
||||
sink.put4(0);
|
||||
''')
|
||||
|
||||
# XX+rd iq with Abs8 globalsym relocation.
|
||||
got_gvaddr8 = TailRecipe(
|
||||
'got_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
|
||||
|
||||
Reference in New Issue
Block a user