/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2009 by Trifork * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package erjang.beam.analysis; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.objectweb.asm.Type; import com.trifork.clj_ds.IPersistentMap; import com.trifork.clj_ds.PersistentHashMap; import erjang.EObject; import erjang.ETuple; @SuppressWarnings("rawtypes") class TypeMap { private static Logger log = Logger.getLogger("erjang.beam"); private static Type TYPE_EOBJECT = Type.getType(EObject.class); private static Type TYPE_ETUPLE = Type.getType(ETuple.class); private static String ETUPLE_NAME = TYPE_ETUPLE.getInternalName(); private static Type[] NO_TYPES = new Type[0]; private static IPersistentMap NO_XTYPES = PersistentHashMap.EMPTY; private final IPersistentMap xregs; private final Type[] yregs, fregs; final int stacksize; // number of y-regs final BasicBlock bb; final ExceptionHandler exh; public TypeMap(BasicBlock bb) { xregs = NO_XTYPES; yregs = NO_TYPES; fregs = NO_TYPES; stacksize = 0; this.bb = bb; exh = null; } public String toString() { StringBuilder sb = new StringBuilder("["); boolean first = true; IPersistentMap xregs = this.xregs; first = printRegs(sb, first, (Map<Integer,Type>)xregs, 'x'); first = printRegs(sb, first, fregs, 'f'); first = printYRegs(sb, first); sb.append(", s=" + stacksize); sb.append(']'); return sb.toString(); } private boolean printRegs(StringBuilder sb, boolean first, Type[] regs, char reg) { for (int i = 0; i < regs.length; i++) { if (regs[i] != null) { if (first == false) { sb.append(", "); } else { first = false; } sb.append(reg).append(i).append(':'); sb.append(shortName(regs[i])); } } return first; } private boolean printRegs(StringBuilder sb, boolean first, Map<Integer,Type> regs, char reg) { for (int i : regs.keySet()) { if (regs.get(i) != null) { if (first == false) { sb.append(", "); } else { first = false; } sb.append(reg).append(i).append(':'); sb.append(shortName(regs.get(i))); } } return first; } private boolean printYRegs(StringBuilder sb, boolean first) { for (int i = 0; i < stacksize; i++) { if (i >= yregs.length) continue; if (yregs[i] != null) { if (first == false) { sb.append(", "); } else { first = false; } sb.append('y').append(get_ypos(i)).append(':'); sb.append(shortName(yregs[i])); } } return first; } private String shortName(Type type) { if (type.getSort() != Type.OBJECT) { return type.getDescriptor(); } String in = type.getInternalName(); if (type.getSort() == Type.OBJECT) { int idx = in.lastIndexOf('/'); String sh = in.substring(idx + 1); if (sh.length() > 6 && sh.startsWith("ETuple")) { return "T" + sh.substring(6); } else { return sh.substring(1, 3); } } else { return in; } } private TypeMap(IPersistentMap xregs, Type[] yregs, Type[] fregs, int stacksize, BasicBlock bb, ExceptionHandler exh) { super(); this.xregs = xregs; this.yregs = yregs; this.fregs = fregs; this.stacksize = stacksize; this.bb = bb; this.exh = exh; if (stacksize > 1023) { log.warning("stacksize > 1023!"); } } public boolean equals(Object obj) { if (obj instanceof TypeMap) { TypeMap other = (TypeMap) obj; return eq((Map)xregs, (Map)other.xregs) && eqy(this, other) && eq(fregs, other.fregs) && eq(exh, other.exh); } return false; } private static boolean eqy(TypeMap me, TypeMap other) { if (me.stacksize != other.stacksize) return false; return eqy_prefix(me, other, Math.min(me.stacksize, other.stacksize)); } private static boolean eqy_prefix(TypeMap me, TypeMap other, int count) { for (int i = 0; i < count; i++) { if (!eq(me.gety(i), other.gety(i))) return false; } return true; } private boolean eq(Map m1, Map m2) { return m1.equals(m2); } private boolean eq(Type[] r1, Type[] r2) { int max = Math.max(r1.length, r2.length); for (int i = 0; i < max; i++) { if (!eq(get(r1, i), get(r2, i))) return false; } return true; } private static boolean eq(Type t1, Type t2) { if (t1 == t2) return true; if (t1 == null || t2 == null) return false; return t1.equals(t2); } private static boolean eq(ExceptionHandler e1, ExceptionHandler e2) { return (e1==e2) || (e1 != null && e1.equals(e2)); } public TypeMap mergeFrom(TypeMap other) { IPersistentMap new_x = eq((Map)xregs, (Map)other.xregs) ? xregs : merge_regs(xregs, other.xregs); Type[] new_f = eq(fregs, other.fregs) ? fregs : merge_regs(fregs, other.fregs); Type[] new_y = yregs; int new_stacksize = Math.min(stacksize, other.stacksize); if (!eqy_prefix(this, other, new_stacksize)) { new_y = new Type[new_stacksize]; boolean differs_from_my_y = false; for (int i = 0; i < new_stacksize; i++) { Type t1; Type res = merge(t1 = this.gety(i), other.gety(i)); new_y[new_stacksize - i - 1] = res; differs_from_my_y = differs_from_my_y || (res!=t1 && (res==null || !res.equals(t1))); } if (!differs_from_my_y) new_y = yregs; } ExceptionHandler new_exh; try { new_exh = ExceptionHandler.merge(exh, other.exh); } catch (IllegalArgumentException iae) { new_exh = ExceptionHandler.Ambiguous.make(exh, other.exh); } if (new_x == xregs && new_y == yregs && new_f == fregs && new_stacksize == stacksize && new_exh == exh) { return this; } else { return new TypeMap(new_x, new_y, new_f, new_stacksize, bb, new_exh); } } private Type[] merge_regs(Type[] r1, Type[] r2) { Type[] res = new Type[Math.max(r1.length, r2.length)]; boolean differs_from_r1 = false; for (int i = 0; i < res.length; i++) { Type t1 = get(r1, i); Type t2 = get(r2, i); res[i] = merge(t1, t2); differs_from_r1 = differs_from_r1 || (res[i]!=t1 && (res[i]==null || !res[i].equals(t1))); } assert(eq(r1,res) || differs_from_r1); return differs_from_r1 ? res : r1; } private IPersistentMap merge_regs(IPersistentMap r1, IPersistentMap r2) { IPersistentMap out = PersistentHashMap.EMPTY; for (Integer r : ((Map<Integer,Type>)r1).keySet()) { Type t1 = (Type) r1.valAt(r); Type t2 = (Type) r2.valAt(r); Type m = merge(t1,t2); if (m != null) { out = out.assoc(r, m); } } return out; } private Type merge(Type t1, Type t2) { if (t1 == null || t2 == null) return null; if (t1.equals(t2)) return t1; if (t1.getSort() == Type.OBJECT && t2.getSort() == Type.OBJECT) { if (t1.getInternalName().startsWith(ETUPLE_NAME) && t2.getInternalName().startsWith(ETUPLE_NAME)) { return TYPE_ETUPLE; } } return TYPE_EOBJECT; } private Type get(Type[] regs, int i) { if (i < regs.length) return regs[i]; else return null; } public TypeMap setx(int reg, Type t, XRegMarker marker) { marker.mark_xreg_as_used(reg); bb.kill_x(reg); if (eq(getx(reg), t)) return this; IPersistentMap new_xregs = xregs.assoc(reg, t); return new TypeMap(new_xregs, yregs, fregs, stacksize, bb, exh); } public TypeMap setf(int reg, Type t) { bb.kill_fr(reg); if (eq(getf(reg), t)) return this; Type[] new_fregs; if (fregs.length <= reg) { new_fregs = grow(fregs, reg); } else { new_fregs = copy(fregs); } new_fregs[reg] = t; return new TypeMap(xregs, yregs, new_fregs, stacksize, bb, exh); } private Type[] copy(Type[] regs) { Type[] res = new Type[regs.length]; for (int i = 0; i < regs.length; i++) { res[i] = regs[i]; } return res; } private static Type[] grow(Type[] regs, int reg) { Type[] res = new Type[reg + 6]; for (int i = 0; i < regs.length; i++) { res[i] = regs[i]; } return res; } public Type getx(int reg) { bb.use_x(reg); return (Type) xregs.valAt(reg, null); } public Type getf(int reg) { bb.use_fr(reg); if (reg >= fregs.length) { return null; } else { return fregs[reg]; } } public TypeMap sety(int reg, Type t) { bb.kill_y(this, reg); if (eq(gety(reg), t)) return this; int pos = get_ypos(reg); if (pos < 0 || pos >= stacksize) throw new IllegalArgumentException("No Y" + reg + " register here."); Type[] new_yregs; if (yregs.length <= pos) { new_yregs = grow(yregs, pos); } else { new_yregs = copy(yregs); } new_yregs[pos] = t; return new TypeMap(xregs, new_yregs, fregs, stacksize, bb, exh); } public Type gety(int reg) { bb.use_y(this, reg); int pos = get_ypos(reg); if (pos < 0 || pos >= stacksize) throw new IllegalArgumentException("no Y" + reg + " register"); if (pos >= yregs.length) return null; return yregs[pos]; } public int get_ypos(int reg) { if (reg < 0 || reg >= stacksize) throw new IllegalArgumentException("No Y" + reg + " register here."); return stacksize - reg - 1; } public TypeMap trim_y(int howmuch) { return new TypeMap(xregs, yregs, fregs, stacksize - howmuch, bb, exh); } public TypeMap alloc_y(int howmuch) { return new TypeMap(xregs, yregs, fregs, stacksize + howmuch, bb, exh); } public TypeMap clearLive(BasicBlock bb) { return new TypeMap(xregs, yregs, fregs, stacksize, bb, exh); } public TypeMap pushExceptionHandler(int handlerLabel) { ExceptionHandler new_exh = ExceptionHandler.push(exh,handlerLabel); return new TypeMap(xregs, yregs, fregs, stacksize, bb, new_exh); } public TypeMap popExceptionHandler() { ExceptionHandler new_exh = (exh instanceof ExceptionHandler.Ambiguous)? null : ExceptionHandler.pop(exh); // Temporary fix - to avoid throws return new TypeMap(xregs, yregs, fregs, stacksize, bb, new_exh); } public void add_succ(BasicBlock succ) { bb.succ(succ); } public void touchx(int from, int to) { for (int i = from; i < to; i++) { bb.use_x(i); } } public Set<Integer> allXregs() { return ((Map)xregs).keySet(); } public int max_freg() { int max = 0; for (int i = 0; i < fregs.length; i++) { if (fregs[i] != null) max = i; } return max + 1; } interface XRegMarker { void mark_xreg_as_used(int reg); } }