From c7826af5a0403b48eeeb6c18532dc076146b1966 Mon Sep 17 00:00:00 2001
From: Mike Pall
Date: Mon, 27 Aug 2012 20:52:15 +0200
Subject: [PATCH] FFI: Detect type punning through unions.
---
doc/ext_ffi_semantics.html | 12 +++++++++---
src/lj_opt_mem.c | 36 ++++++++++++++++++++----------------
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/doc/ext_ffi_semantics.html b/doc/ext_ffi_semantics.html
index afe7e613..56b3f62e 100644
--- a/doc/ext_ffi_semantics.html
+++ b/doc/ext_ffi_semantics.html
@@ -192,8 +192,8 @@ a typedef, except re-declarations will be ignored):
-You're encouraged to use these types in preference to the
-compiler-specific extensions or the target-dependent standard types.
+You're encouraged to use these types in preference to
+compiler-specific extensions or target-dependent standard types.
E.g. char differs in signedness and long differs in
size, depending on the target architecture and platform ABI.
@@ -660,12 +660,18 @@ initialization. The JIT compiler benefits from this fact when applying
certain optimizations.
-As a consequence of this, the elements of complex numbers and
+As a consequence, the elements of complex numbers and
vectors are immutable. But the elements of an aggregate holding these
types may be modified of course. I.e. you cannot assign to
foo.c.im, but you can assign a (newly created) complex number
to foo.c.
+
+The JIT compiler implements strict aliasing rules: accesses to different
+types do not alias, except for differences in signedness (this
+applies even to char pointers, unlike C99). Type punning
+through unions is explicitly detected and allowed.
+
Calling a cdata object
diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c
index a7e0d35e..560c4ade 100644
--- a/src/lj_opt_mem.c
+++ b/src/lj_opt_mem.c
@@ -601,17 +601,8 @@ static AliasRet aa_xref(jit_State *J, IRIns *refa, IRIns *xa, IRIns *xb)
ptrdiff_t ofsa = 0, ofsb = 0;
IRIns *refb = IR(xb->op1);
IRIns *basea = refa, *baseb = refb;
- /* This implements (very) strict aliasing rules.
- ** Different types do NOT alias, except for differences in signedness.
- ** NYI: this also prevents type punning through unions.
- */
- if (irt_sametype(xa->t, xb->t)) {
- if (refa == refb)
- return ALIAS_MUST; /* Shortcut for same refs with identical type. */
- } else if (!(irt_typerange(xa->t, IRT_I8, IRT_U64) &&
- ((xa->t.irt - IRT_I8) ^ (xb->t.irt - IRT_I8)) == 1)) {
- return ALIAS_NO;
- }
+ if (refa == refb && irt_sametype(xa->t, xb->t))
+ return ALIAS_MUST; /* Shortcut for same refs with identical type. */
/* Offset-based disambiguation. */
if (refa->o == IR_ADD && irref_isk(refa->op2)) {
IRIns *irk = IR(refa->op2);
@@ -629,12 +620,25 @@ static AliasRet aa_xref(jit_State *J, IRIns *refa, IRIns *xa, IRIns *xb)
if (refa == baseb && ofsb != 0)
return ALIAS_NO; /* base vs. base+-ofs. */
}
+ /* This implements (very) strict aliasing rules.
+ ** Different types do NOT alias, except for differences in signedness.
+ ** Type punning through unions is allowed (but forces a reload).
+ */
if (basea == baseb) {
- /* This assumes strictly-typed, non-overlapping accesses. */
- if (ofsa != ofsb)
- return ALIAS_NO; /* base+-o1 vs. base+-o2 and o1 != o2. */
- return ALIAS_MUST; /* Unsigned vs. signed access to the same address. */
+ ptrdiff_t sza = irt_size(xa->t), szb = irt_size(xb->t);
+ if (ofsa == ofsb) {
+ if (sza == szb && irt_isfp(xa->t) == irt_isfp(xb->t))
+ return ALIAS_MUST; /* Same-sized, same-kind. May need to convert. */
+ } else if (ofsa + sza <= ofsb || ofsb + szb <= ofsa) {
+ return ALIAS_NO; /* Non-overlapping base+-o1 vs. base+-o2. */
+ }
+ /* NYI: extract, extend or reinterpret bits (int <-> fp). */
+ return ALIAS_MAY; /* Overlapping or type punning: force reload. */
}
+ if (!irt_sametype(xa->t, xb->t) &&
+ !(irt_typerange(xa->t, IRT_I8, IRT_U64) &&
+ ((xa->t.irt - IRT_I8) ^ (xb->t.irt - IRT_I8)) == 1))
+ return ALIAS_NO;
/* NYI: structural disambiguation. */
return aa_cnew(J, basea, baseb); /* Try to disambiguate allocations. */
}
@@ -730,7 +734,7 @@ retry:
if (st == IRT_I8 || st == IRT_I16) { /* Trunc + sign-extend. */
st |= IRCONV_SEXT;
} else if (st == IRT_U8 || st == IRT_U16) { /* Trunc + zero-extend. */
- } else if (st == IRT_INT && !irt_isint(IR(store->op2)->t)) {
+ } else if (st == IRT_INT) {
st = irt_type(IR(store->op2)->t); /* Needs dummy CONV.int.*. */
} else { /* I64/U64 are boxed, U32 is hidden behind a CONV.num.u32. */
goto store_fwd;