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:
Dan Gohman
2018-04-13 15:00:09 -07:00
committed by GitHub
parent 645fa3e858
commit 0e57f3d0ea
46 changed files with 312 additions and 164 deletions

View 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',))

View File

@@ -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())

View File

@@ -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(

View File

@@ -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')

View File

@@ -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))

View File

@@ -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,