diff options
| author | Michael Matz <matz@suse.de> | 2017-11-15 13:39:28 +0100 |
|---|---|---|
| committer | Michael Matz <matz@suse.de> | 2017-11-15 13:39:28 +0100 |
| commit | 348dd9f4a68a5a6217142c64070765733af3a8c8 (patch) | |
| tree | 760ecdeae467eb25afb5a9d52914f8c993ec7e39 /x86_64-gen.c | |
| parent | 74463eb954bf7c579915ea2becdc1483359dacfc (diff) | |
| download | tinycc-348dd9f4a68a5a6217142c64070765733af3a8c8.tar.gz tinycc-348dd9f4a68a5a6217142c64070765733af3a8c8.tar.bz2 | |
Fix absolute memory references
This properly fixes what 870271ea tried to fix. Absolute memory
references can't use %rip relative addressing, and additionally,
if the address doesn't fit 32bit (signed) it must be loaded via
movabs. No good testcase added, it would require catching signals
and still be unreliable.
Diffstat (limited to 'x86_64-gen.c')
| -rw-r--r-- | x86_64-gen.c | 31 |
1 files changed, 25 insertions, 6 deletions
diff --git a/x86_64-gen.c b/x86_64-gen.c index 4d063cb..d7cb895 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -288,12 +288,18 @@ static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got) op_reg = REG_VALUE(op_reg) << 3; if ((r & VT_VALMASK) == VT_CONST) { /* constant memory reference */ - o(0x05 | op_reg); - if (is_got) { - gen_gotpcrel(r, sym, c); - } else { - gen_addrpc32(r, sym, c); - } + if (!(r & VT_SYM)) { + /* Absolute memory reference */ + o(0x04 | op_reg); /* [sib] | destreg */ + oad(0x25, c); /* disp32 */ + } else { + o(0x05 | op_reg); /* (%rip)+disp32 | destreg */ + if (is_got) { + gen_gotpcrel(r, sym, c); + } else { + gen_addrpc32(r, sym, c); + } + } } else if ((r & VT_VALMASK) == VT_LOCAL) { /* currently, we use only ebp as base */ if (c == (char)c) { @@ -381,6 +387,19 @@ void load(int r, SValue *sv) fr = get_reg(RC_INT); load(fr, &v1); } + if (fc != sv->c.i) { + /* If the addends doesn't fit into a 32bit signed + we must use a 64bit move. We've checked above + that this doesn't have a sym associated. */ + v1.type.t = VT_LLONG; + v1.r = VT_CONST; + v1.c.i = sv->c.i; + fr = r; + if (!(reg_classes[fr] & (RC_INT|RC_R11))) + fr = get_reg(RC_INT); + load(fr, &v1); + fc = 0; + } ll = 0; /* Like GCC we can load from small enough properly sized structs and unions as well. |
