/**
* 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.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.asm.Type;
import erjang.EAtom;
import erjang.EBig;
import erjang.EBinMatchState;
import erjang.EBinary;
import erjang.EBitString;
import erjang.ECons;
import erjang.EDouble;
import erjang.EFun;
import erjang.EInteger;
import erjang.EList;
import erjang.EString;
import erjang.ENil;
import erjang.ENumber;
import erjang.EObject;
import erjang.EPID;
import erjang.EPort;
import erjang.ERT;
import erjang.ERef;
import erjang.ESeq;
import erjang.ESmall;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.beam.Arg;
import erjang.beam.BIFUtil;
import erjang.beam.BeamCodeBlock;
import erjang.beam.BeamFunction;
import erjang.beam.BeamInstruction;
import erjang.beam.BeamOpcode;
import erjang.beam.BeamExceptionHandler;
import erjang.beam.BlockVisitor;
import erjang.beam.BlockVisitor2;
import erjang.beam.BuiltInFunction;
import erjang.beam.ExtFunc;
import erjang.beam.FunctionAdapter;
import erjang.beam.FunctionVisitor;
import erjang.beam.FunctionVisitor2;
import erjang.beam.ModuleAdapter;
import erjang.beam.ModuleVisitor;
import erjang.beam.Arg.Kind;
import erjang.beam.repr.Insn;
import erjang.beam.repr.ExtFun;
import erjang.beam.repr.Insn.S;
import erjang.beam.repr.Operands;
import erjang.beam.repr.Operands.Int;
import erjang.beam.repr.Operands.SourceOperand;
import erjang.beam.repr.Operands.XReg;
import static erjang.beam.repr.Operands.SourceOperand;
import static erjang.beam.repr.Operands.DestinationOperand;
import static erjang.beam.CodeAtoms.*;
public class BeamTypeAnalysis extends ModuleAdapter {
static final Logger log = Logger.getLogger("erjang.beam");
/**
*
*/
public BeamTypeAnalysis(ModuleVisitor mv) {
super(mv);
}
static final Type ESMALL_TYPE = Type.getType(ESmall.class);
static final Type EBIG_TYPE = Type.getType(EBig.class);
static final Type EINTEGER_TYPE = Type.getType(EInteger.class);
static final Type ENUMBER_TYPE = Type.getType(ENumber.class);
static final Type EOBJECT_TYPE = Type.getType(EObject.class);
static final Type EDOUBLE_TYPE = Type.getType(EDouble.class);
static final Type ENIL_TYPE = Type.getType(ENil.class);
static final Type EATOM_TYPE = Type.getType(EAtom.class);
static final Type ETUPLE_TYPE = Type.getType(ETuple.class);
static final Type EBINARY_TYPE = Type.getType(EBinary.class);
static final Type EBITSTRING_TYPE = Type.getType(EBitString.class);
static final Type ECONS_TYPE = Type.getType(ECons.class);
static final Type ESEQ_TYPE = Type.getType(ESeq.class);
static final Type ELIST_TYPE = Type.getType(EList.class);
static final Type EFUN_TYPE = Type.getType(EFun.class);
static final Type EPID_TYPE = Type.getType(EPID.class);
static final Type EPORT_TYPE = Type.getType(EPort.class);
static final Type EREFERENCE_TYPE = Type.getType(ERef.class);
static final Type EMATCHSTATE_TYPE = Type.getType(EBinMatchState.class);
static final EAtom am_plus = EAtom.intern("+");
static final EAtom am_minus = EAtom.intern("-");
private static final ETuple X0_REG = ETuple.make(new EObject[] { X_ATOM,
new ESmall(0) });
private EAtom moduleName;
private List<FV> functions = new ArrayList<FV>();
@Override
public void declareFunction(EAtom fun, int arity, int label) {
/* ignore */
}
@Override
public FunctionVisitor visitFunction(EAtom name, int arity, int startLabel) {
FV f = new FV(super.visitFunction(name, arity, startLabel), name,
arity, startLabel);
functions.add(f);
return f;
}
class FV extends FunctionAdapter implements BeamFunction, TypeMap.XRegMarker {
BasicBlock makeBasicBlock(int label, int index) {
assert ((label & 0xffff) == label);
assert ((index & 0xffff) == index);
int bbk = (label << 16) | index;
BasicBlock bb = bbs.get(bbk);
if (bb == null) {
bbs.put(bbk, bb = new BasicBlock(label, index));
}
return bb;
}
TreeMap<Integer, BasicBlock> bbs = new TreeMap<Integer, BasicBlock>();
/** Maps from label to list of exception handler labels. */
TreeMap<Integer, List<BeamExceptionHandler>> blocks_with_ambiguous_exh = new TreeMap<Integer, List<BeamExceptionHandler>>();
void live_analysis() {
Integer[] all = bbs.keySet().toArray(new Integer[bbs.size()]);
Arrays.sort(all);
boolean change;
int iter = 0;
do {
change = false;
iter += 1;
for (int n = all.length - 1; n >= 0; n--) {
BasicBlock bb = bbs.get(all[n]);
BitSet inq = bb.in;
BitSet outq = bb.out;
BitSet out_n = new BitSet();
for (BasicBlock s : bb.succ) {
BitSet in_s = s.in;
if (in_s != null) {
out_n.or(in_s);
}
}
bb.out = out_n;
BitSet in_n = (BitSet) out_n.clone();
in_n.andNot(bb.def);
in_n.or(bb.use);
bb.in = in_n;
change |= (!inq.equals(in_n)) || (!outq.equals(out_n));
}
} while (change);
// log.finer("live analysis for " + name + "/" + arity
// + " completed in " + iter + " iterations.");
}
Map<Integer, LabeledBlock> lbs = new TreeMap<Integer, LabeledBlock>();
private final EAtom name;
private final int arity;
private final int startLabel;
private SortedSet<LabeledBlock> needs_analyze = new TreeSet<LabeledBlock>(
new Comparator<LabeledBlock>() {
@Override
public int compare(LabeledBlock o1, LabeledBlock o2) {
return o2.block_label - o1.block_label;
}
});
public int max_stack;
public HashSet<Integer> all_xregs = new HashSet<Integer>();
public int max_freg;
private boolean is_tail_recursive;
public FV(FunctionVisitor fv, EAtom name, int arity, int startLabel) {
super(fv);
this.name = name;
this.arity = arity;
this.startLabel = startLabel;
}
@Override
public void visitEnd() {
LabeledBlock lb = lbs.get(startLabel);
lb.merge_from(this.make_initial());
try {
while (!needs_analyze.isEmpty()) {
lb = needs_analyze.first();
needs_analyze.remove(lb);
lb.analyze();
}
} catch (RuntimeException t) {
dump();
throw t;
} catch (Error t) {
dump();
throw t;
}
// woo!
// live_analysis();
SortedSet<Integer> labels = new TreeSet<Integer>();
labels.addAll(lbs.keySet());
for (int i : labels) {
lb = lbs.get(i);
if (lb.initial == null) continue;
ExceptionHandler e = lb.initial.exh;
if (e == null) continue;
List<BeamExceptionHandler> ambi = e.ambiguousities();
if (ambi != null) {
blocks_with_ambiguous_exh.put(i,ambi);
}
}
List<Integer> dead = new ArrayList<Integer>();
boolean has_unreachable_code = false;
for (int i : labels) {
lb = lbs.get(i);
if (lb.isDeadCode()) {
dead.add(i);
if (lb.insns.get(0).opcode() == BeamOpcode.func_info) {
// ignore this
} else {
log.fine("UNREACHABLE " + lb.block_label);
has_unreachable_code = true;
}
}
}
if (has_unreachable_code) {
this.dump();
}
function_visit_end(dead);
}
private void function_visit_end(List<Integer> dead) {
if (fv instanceof FunctionVisitor2) {
((FunctionVisitor2) fv).visitMaxs((Collection<Integer>) this.all_xregs ,
this.max_stack, this.max_freg, this.is_tail_recursive, dead);
}
for (LabeledBlock block : this.lbs.values()) {
ExceptionHandler block_exh =
block.initial==null? null : block.initial.exh;
assert(blocks_with_ambiguous_exh.containsKey(block.block_label) == (block_exh != null && block_exh.ambiguousities() != null));
if (block_exh != null && block_exh.ambiguousities() != null) {
// Handle block with ambiguous exception handler:
List<BeamExceptionHandler> ambi = blocks_with_ambiguous_exh.get(block.block_label);
for (BeamExceptionHandler e : ambi) {
int ext_label = extendedLabel(block.block_label, e);
function_visit_end_aux(block, ext_label, e);
}
} else {
int ext_label = extendedLabel(block.block_label, block_exh);
function_visit_end_aux(block, ext_label, block_exh);
}
}
super.fv.visitEnd();
}
private void function_visit_end_aux(LabeledBlock block, int ext_label, BeamExceptionHandler exh) {
BlockVisitor vis = super.fv.visitLabeledBlock(ext_label);
try {
block.accept(vis, exh);
} catch (Error e) {
dump();
throw e;
}
}
private void dump() {
if (log.isLoggable(Level.FINER)) {
log.finer("DUMPING " + name + "/" + arity);
for (Map.Entry<Integer, LabeledBlock> ent : lbs.entrySet()) {
ent.getValue().dump();
}
}
}
@Override
public BlockVisitor visitLabeledBlock(int label) {
return get_lb(label, true);
}
private TypeMap make_initial() {
TypeMap res = new TypeMap(makeBasicBlock(startLabel, 0));
for (int i = 0; i < arity; i++) {
res = res.setx(i, EOBJECT_TYPE, this);
}
return res;
}
private LabeledBlock get_lb(int label, boolean create) {
if (lbs.containsKey(label)) {
LabeledBlock res = lbs.get(label);
return res;
} else if (create) {
// System.out.println("creating " + name + "/" + arity + ":" +
// label);
LabeledBlock res = new LabeledBlock(label);
lbs.put(label, res);
return res;
} else {
return null;
}
}
int extendedLabel(int label, BeamExceptionHandler exh) {
assert ((label &~ 0xffff) == 0);
int extLabel = label;
if (blocks_with_ambiguous_exh.containsKey(label)) {
// Add info about exception context:
if (exh != null) {
assert(! blocks_with_ambiguous_exh.containsKey(exh.getHandlerLabel()));
int handlerLabel = exh.getHandlerLabel();
assert ((handlerLabel &~ 0xffff) == 0);
extLabel |= (handlerLabel << 16);
} // else high 16 bits are 0.
}
return extLabel;
}
public void mark_xreg_as_used(int reg) {
all_xregs.add(reg);
}
private void update_max_regs(TypeMap current) {
max_stack = Math.max(max_stack, current.stacksize);
max_freg = Math.max(max_freg, current.max_freg());
/* As for the X registers, we wish to track their usage
* individually (rather than just the number of the
* highest one used).
* This set could be updated here, but it is far more
* efficient to do it elsewhere - see TypeMap.setx().
*/
}
class LabeledBlock implements BlockVisitor, BeamCodeBlock {
private final int block_label;
TypeMap initial;
boolean last = false;
TypeMap[] map;
public LabeledBlock(int label) {
this.block_label = label;
initial = null;
}
/**
* @param vis
*/
public void accept(BlockVisitor vis, BeamExceptionHandler exh) {
try {
if (!isDeadCode()) {
if (vis instanceof BlockVisitor2) {
accept_2((BlockVisitor2) vis, exh);
} else {
accept_1(vis);
}
}
} finally {
vis.visitEnd();
}
}
private void accept_1(BlockVisitor vis) {
throw new erjang.NotImplemented();
/*
for (int i = 0; i < insns.size(); i++) {
Insn insn = insns.get(i);
BeamOpcode opcode = insn.opcode();
vis.visitInsn(opcode, insn);
}
*/
}
private void accept_2(BlockVisitor2 vis, BeamExceptionHandler exh) {
int tuple_pos = 0;
Arg tuple_reg = null;
vis.visitBegin(exh);
for (int insn_idx = 0; insn_idx < insns.size(); insn_idx++) {
Insn insn_ = insns.get(insn_idx);
BeamOpcode opcode = insn_.opcode();
TypeMap type_map = this.map[insn_idx];
switch (opcode) {
case func_info: {
Insn.AAI insn = (Insn.AAI)insn_;
// log.finer("go: " + insn);
vis.visitInsn(opcode, insn.getExtFun());
break;
}
case fnegate: {
Insn.LSD insn = (Insn.LSD)insn_;
EAtom name = opcode.symbol;
int failLabel = decode_labelref(insn.label, type_map.exh);
Arg[] in = new Arg[] {src_arg(insn_idx, insn.src)};
Type[] inTypes = new Type[] {in[0].type};
Arg out = dest_arg(insn_idx, insn.dest);
BuiltInFunction bif = BIFUtil.getMethod("erlang", name.getName(), inTypes,
failLabel != 0, true);
vis.visitInsn(opcode, failLabel, in, out, bif);
break;
}
case fconv:
case fmove:
case move: {
Insn.SD insn = (Insn.SD)insn_;
Arg src = src_arg(insn_idx, insn.src);
Arg dest = dest_arg(insn_idx, insn.dest);
if (insns.size() > insn_idx+1) {
Insn next_insn = insns.get(insn_idx+1);
if (next_insn.opcode() == BeamOpcode.K_return) {
vis.visitInsn(BeamOpcode.K_return, src);
insn_idx += 1;
break;
}
}
if (dest.kind != Kind.F) {
if (src.kind == Kind.F) {
dest = new Arg(dest, EDOUBLE_TYPE);
} else {
dest = new Arg(dest, src.type);
}
} else {
// arg2.kind == F
}
vis.visitInsn(opcode, src, dest);
break;
}
case put_string: {
Insn.ByD insn = (Insn.ByD)insn_;
Arg src = src_arg(insn_idx, insn.bin);
Arg dest = dest_arg(insn_idx, insn.dest);
// Arg arg1 = decode_arg(insn_idx, insn.elm(3));
// Arg arg2 = decode_out_arg(insn_idx, insn.elm(4));
dest = new Arg(dest, ESEQ_TYPE);
vis.visitInsn(BeamOpcode.move, src, dest);
break;
}
case fadd:
case fsub:
case fmul:
case fdiv:
{
Insn.LSSD insn = (Insn.LSSD)insn_;
EAtom name = opcode.symbol;
int failLabel = decode_labelref(insn.label, type_map.exh);
Arg[] in = new Arg[] {src_arg(insn_idx, insn.src1),
src_arg(insn_idx, insn.src2)};
Type[] inTypes = new Type[] {in[0].type,
in[1].type};
Arg out = dest_arg(insn_idx, insn.dest);
BuiltInFunction bif = BIFUtil.getMethod("erlang", name.getName(), inTypes,
failLabel != 0, true);
vis.visitInsn(opcode, failLabel, in, out, bif);
break;
}
case bif0:
case bif1:
case bif2:
{
Insn.Bif insn = (Insn.Bif)insn_;
EAtom name = insn.ext_fun.fun;
int failLabel = decode_labelref(insn.label, type_map.exh);
SourceOperand[] srcs = insn.args;
Arg[] in = src_args(insn_idx, srcs);
Arg out = dest_arg(insn_idx, insn.dest);
BuiltInFunction bif = BIFUtil.getMethod("erlang", name.getName(),
parmTypes(type_map, srcs),
failLabel != 0, true);
vis.visitInsn(opcode, failLabel, in, out, bif);
break;
}
case gc_bif1:
case gc_bif2:
case gc_bif3:
{
Insn.GcBif insn = (Insn.GcBif)insn_;
EAtom name = insn.ext_fun.fun;
int failLabel = decode_labelref(insn.label, type_map.exh);
SourceOperand[] srcs = insn.args;
Arg[] in = src_args(insn_idx, srcs);
Arg out = dest_arg(insn_idx, insn.dest);
// special case for X+1, 1+X, X-1.
Int lop = null, rop = null;
if (srcs.length==2
&& (((name==am_plus || name == am_minus) && (rop=srcs[1].testInt()) != null && rop.equals(1))
|| (name==am_plus && (lop=srcs[0].testInt()) != null && lop.equals(1))))
{
if (name == am_plus) {
Arg src = (lop == null) ? in[0] : in[1];
vis.visitIncrement(src, out);
break;
} else if (name == am_minus) {
Arg src = in[0];
vis.visitDecrement(src, out);
break;
}
}
BuiltInFunction bif = BIFUtil.getMethod("erlang", name.getName(),
parmTypes(type_map, srcs),
failLabel != 0, true);
vis.visitInsn(opcode, failLabel, in, out, bif);
break;
}
case is_tuple: {
if (insn_idx+1 < insns.size()) {
Insn next_insn = insns.get(insn_idx+1);
if (next_insn.opcode() == BeamOpcode.test_arity) {
int this_fail = decode_labelref(((Insn.L)insn_).label, this.map[insn_idx].exh);
int next_fail = decode_labelref(((Insn.L)next_insn).label, this.map[insn_idx+1].exh);
if (this_fail == next_fail) {
Arg this_arg = src_arg(insn_idx, ((Insn.LD)insn_).dest);
Arg next_arg = src_arg(insn_idx+1, ((Insn.LD)next_insn).dest);
if (this_arg.equals(next_arg)) {
// SKIP THIS INSTRUCTION!
break;
}
}
}
}
}
// Tests:
// LS:
case is_integer:
case is_float:
case is_number:
case is_atom:
case is_pid:
case is_reference:
case is_port:
case is_nil:
case is_binary:
case is_list:
case is_nonempty_list:
case is_function:
case is_boolean:
case is_bitstr:
// LSI:
case test_arity:
case bs_test_tail2:
case bs_test_unit:
// LSS:
case is_lt:
case is_ge:
case is_eq:
case is_ne:
case is_eq_exact:
case is_ne_exact:
case is_function2:
// LSBi:
case bs_match_string:
// LSII:
case bs_skip_utf8:
case bs_skip_utf16:
case bs_skip_utf32:
// LSIID:
case bs_start_match2:
case bs_get_utf8:
case bs_get_utf16:
case bs_get_utf32:
// LSSII:
case bs_skip_bits2:
// LSISIID:
case bs_get_integer2:
case bs_get_float2:
case bs_get_binary2:
accept_2_test(vis, (Insn.L)insn_, insn_idx);
break;
case K_return:
vis.visitInsn(opcode, new Arg(Arg.Kind.X, 0,
type_map.getx(0)));
break;
case allocate_heap_zero:
case allocate_zero: {
Insn.I insn = (Insn.I)insn_;
int depth = type_map.stacksize;
int count = insn.i1;
Arg[] ys = new Arg[count];
for (int i = 0; i < count; i++) {
ys[i] = new Arg(Arg.Kind.Y, depth + i, null);
}
vis.visitInsn(opcode, (Arg[]) ys);
break;
}
case test_heap:
break;
case fclearerror:
case fcheckerror:
break;
case recv_mark:
case recv_set:
break;
case call_ext_last:
case call_ext_only:
do_call(vis, insn_idx, (Insn.I)insn_, true, true);
break;
case call_ext:
do_call(vis, insn_idx, (Insn.I)insn_, false, true);
if (is_exceptional_call(insn_)) {
vis.visitUnreachablePoint();
}
break;
case call:
do_call(vis, insn_idx, (Insn.I)insn_, false, false);
break;
case call_last:
case call_only:
do_call(vis, insn_idx, (Insn.I)insn_, true, false);
break;
case apply_last:
case apply: {
Insn.I insn = (Insn.I) insn_;
Arg[] args = new Arg[2 + insn.i1];
for (int i = 0; i < args.length; i++) {
args[i] = new Arg(Arg.Kind.X, i, map[insn_idx]
.getx(i));
}
vis.visitInsn(opcode, args);
break;
}
case make_fun2: {
Insn.F insn = (Insn.F) insn_;
ExtFun efun = insn.anon_fun.asExtFun();
int numfree = insn.anon_fun.free_vars;
int index = insn.anon_fun.index;
int old_index = insn.anon_fun.old_index;
EBinary uniq = insn.anon_fun.mod_md5;
int old_uniq = insn.anon_fun.old_uniq;
Arg[] free = new Arg[numfree];
for (int i = 0; i < numfree; i++) {
free[i] = new Arg(Arg.Kind.X, i, map[insn_idx]
.getx(i));
}
vis.visitInsn(opcode, efun, free, index, old_index, uniq, old_uniq);
break;
}
case init: {
Insn.D insn = (Insn.D) insn_;
vis.visitInsn(opcode, dest_arg(insn_idx, insn.dest));
break;
}
case put_list: {
Insn.SSD insn = (Insn.SSD) insn_;
Arg[] in = new Arg[] {
src_arg(insn_idx, insn.src1),
src_arg(insn_idx, insn.src2)
};
Arg out = dest_arg(insn_idx, insn.dest);
vis.visitInsn(opcode, in, out);
break;
}
case put_tuple: {
Insn.ID insn = (Insn.ID) insn_;
int arity = insn.i1;
Arg[] puts = new Arg[arity];
boolean simple_tuple_create = true;
for (int i = 1; i <= arity; i++) {
if (insns.get(insn_idx+i).opcode() == BeamOpcode.put) {
Insn.S put_insn = (Insn.S) insns.get(insn_idx+i);
puts[i-1] = src_arg(insn_idx+i, put_insn.src);
continue;
}
simple_tuple_create = false;
break;
}
if (simple_tuple_create) {
tuple_reg = new Arg(dest_arg(insn_idx, insn.dest),
getTupleType(arity));
insn_idx += arity;
vis.visitMakeTuple(arity, tuple_reg, puts);
} else {
tuple_reg = new Arg(dest_arg(insn_idx, insn.dest),
getTupleType(arity));
vis.visitInsn(opcode, arity, tuple_reg);
tuple_pos = 1;
}
break;
}
case put: {
Insn.S insn = (Insn.S) insn_;
Arg val = src_arg(insn_idx, insn.src);
vis.visitInsn(opcode, val, tuple_reg, tuple_pos++);
break;
}
case set_tuple_element: {
Insn.SDI insn = (Insn.SDI) insn_;
Arg in = src_arg(insn_idx, insn.src);
Arg out = src_arg(insn_idx, insn.dest);
int idx = insn.i;
vis.visitInsn(opcode, in, out, idx);
break;
}
case allocate_heap:
case allocate:
case deallocate: {
// need to zero out refs?
break;
}
case select_tuple_arity: {
Insn.Select insn = (Insn.Select) insn_;
int failLabel = decode_labelref(insn.defaultLabel,
type_map.exh);
Arg in = src_arg(insn_idx, insn.src);
Operands.SelectList jumpTable = insn.jumpTable;
int len = jumpTable.size();
int[] arities = new int[len];
int[] targets = new int[len];
for (int i=0; i<len; i++) {
Operands.Operand value = jumpTable.getValue(i);
Operands.Label target = jumpTable.getLabel(i);
arities[i] = value.asCodeInt().value;
targets[i] = decode_labelref(target, type_map.exh);
}
vis.visitSelectTuple(in, failLabel, arities, targets);
break;
}
case select_val: {
Insn.Select insn = (Insn.Select) insn_;
int failLabel = decode_labelref(insn.defaultLabel,
type_map.exh);
Arg in = src_arg(insn_idx, insn.src);
Operands.SelectList jumpTable = insn.jumpTable;
int len = jumpTable.size();
Arg[] values = new Arg[len];
int[] targets = new int[len];
for (int i=0; i<len; i++) {
Operands.Operand value = jumpTable.getValue(i);
Operands.Label target = jumpTable.getLabel(i);
values[i] = arg(value.asLiteral());
targets[i] = decode_labelref(target, type_map.exh);
}
vis.visitSelectValue(in, failLabel, values, targets);
break;
}
case get_tuple_element: {
Insn.SID insn = (Insn.SID) insn_;
Arg in = src_arg(insn_idx, insn.src);
int idx = insn.i;
Arg out = dest_arg(insn_idx, insn.dest);
vis.visitInsn(opcode, in, out, idx);
break;
}
case jump: {
Insn.L insn = (Insn.L) insn_;
vis.visitJump(decode_labelref(insn.label, type_map.exh));
break;
}
case line: // TODO!
vis.visitLine(((Insn.I)insn_).i1);
case on_load: // ignore
case trim:
break;
case get_list: {
Insn.SDD insn = (Insn.SDD) insn_;
vis.visitInsn(opcode, new Arg[] {
src_arg(insn_idx, insn.src),
dest_arg(insn_idx, insn.dest1),
dest_arg(insn_idx, insn.dest2) });
break;
}
case try_case_end:
case badmatch:
case case_end: {
Insn.S insn = (Insn.S) insn_;
vis.visitInsn(opcode, src_arg(insn_idx, insn.src));
break;
}
case if_end:
vis.visitInsn(opcode);
break;
case send: {
vis.visitInsn(opcode,
new Arg[] { new Arg(Arg.Kind.X, 0),
new Arg(Arg.Kind.X, 1) });
break;
}
case K_try:{
Insn.YL insn = (Insn.YL) insn_;
TypeMap type_map_after = this.map[insn_idx+1];
vis.visitCatchBlockStart(opcode,
decode_labelref(insn.label, type_map.exh),
src_arg(insn_idx, insn.y),
type_map_after.exh);
break;
}
case K_catch: {
Insn.YL insn = (Insn.YL) insn_;
TypeMap type_map_after = this.map[insn_idx+1];
vis.visitCatchBlockStart(opcode,
decode_labelref(insn.label, type_map.exh),
src_arg(insn_idx, insn.y),
type_map_after.exh);
break;
}
case raise: {
Insn.SS insn = (Insn.SS) insn_;
Arg[] in = {src_arg(insn_idx, insn.src1),
src_arg(insn_idx, insn.src2) };
Arg ex = new Arg(Arg.Kind.X, 0);
int failLabel = 0;
// Half of the args are constants...
vis.visitInsn(opcode, failLabel, in, ex);
break;
}
case try_end:
case try_case:
case catch_end: {
Insn.Y insn = (Insn.Y) insn_;
vis.visitCatchBlockEnd(opcode,
src_arg(insn_idx, insn.y),
type_map.exh);
break;
}
case loop_rec: { /* loop receive */
Insn.LD insn = (Insn.LD) insn_;
vis.visitReceive(opcode,
decode_labelref(insn.label, type_map.exh),
dest_arg(insn_idx, insn.dest));
break;
}
case remove_message:
case timeout:
vis.visitInsn(opcode);
break;
case loop_rec_end:
case wait: {
Insn.L insn = (Insn.L) insn_;
vis.visitInsn(opcode, decode_labelref(insn.label, type_map.exh), null);
break;
}
case wait_timeout: {
Insn.LS insn = (Insn.LS) insn_;
vis.visitInsn(opcode,
decode_labelref(insn.label, type_map.exh),
src_arg(insn_idx, insn.src));
break;
}
case call_fun:
case i_call_fun_last: {
Insn.I insn = (Insn.I) insn_;
int nargs = insn.i1;
Arg[] args = new Arg[nargs + 1];
for (int i = 0; i < args.length; i++) {
args[i] = new Arg(Arg.Kind.X, i, map[insn_idx]
.getx(i));
}
vis.visitInsn(opcode, args,
new Arg(Arg.Kind.X, 0, null));
break;
}
// {bs_add,{f,0},[{x,3},{x,4},1],{x,3}}
case bs_add: {
Insn.LSSID insn = (Insn.LSSID) insn_;
vis.visitBSAdd(src_arg(insn_idx, insn.src1),
src_arg(insn_idx, insn.src2),
insn.i3,
dest_arg(insn_idx, insn.dest));
break;
}
case bs_context_to_binary: {
Insn.D insn = (Insn.D) insn_;
vis.visitBS(opcode, dest_arg(insn_idx, insn.dest), null, 0);
// do nothing for now
break;
}
case bs_restore2:
case bs_save2: {
Insn.DI insn = (Insn.DI) insn_;
vis.visitBS(opcode, src_arg(insn_idx, insn.dest),
//TODO: streamline - change API
insn.i2 == -1
? new Arg(EAtom.intern("start"))
: new Arg(new ESmall(insn.i2)), 0
);
break;
}
case bs_init_writable: {
Arg size = src_arg(insn_idx, new Operands.XReg(0));
Arg dest = dest_arg(new Operands.XReg(0));
vis.visitInitWritable(size, dest);
break;
}
case bs_init2:
case bs_init_bits: {
Insn.LSIIID insn = (Insn.LSIIID) insn_;
Arg size = src_arg(insn_idx, insn.src2);
int flags = insn.i5;
Arg out = dest_arg(insn_idx, insn.dest);
boolean unit_is_bits = (opcode == BeamOpcode.bs_init_bits);
vis.visitInitBitString(size, flags, out,
unit_is_bits);
break;
}
case bs_put_string: {
Insn.By insn = (Insn.By) insn_;
Arg str = arg(insn.bin);
vis.visitBitStringPut(opcode, str, null,-1,-1);
break;
}
case bs_put_binary:
case bs_put_integer:
case bs_put_float:
{
Insn.LSIIS insn = (Insn.LSIIS) insn_;
Arg size = src_arg(insn_idx, insn.src2);
int unit = insn.i3;
int flags = insn.i4;
Arg value = src_arg(insn_idx, insn.src5);
vis.visitBitStringPut(opcode, value, size, unit, flags);
break;
}
case bs_put_utf8:
case bs_put_utf16:
case bs_put_utf32:
{
Insn.LIS insn = (Insn.LIS) insn_;
int flags = insn.i2;
Arg value = src_arg(insn_idx, insn.src);
//TODO: is the label always 0? (Op may fail on bad chars)
vis.visitBitStringPut(opcode, value, null, -1, flags);
break;
}
case bs_utf8_size:
case bs_utf16_size: {
Insn.LSD insn = (Insn.LSD) insn_;
Arg value = src_arg(insn_idx, insn.src);
Arg out = dest_arg(insn_idx, insn.dest);
int label = decode_labelref(insn.label, type_map.exh);
vis.visitBS(opcode, value, out, label);
break;
}
case bs_private_append: {
Insn.BSPrivateAppend insn = (Insn.BSPrivateAppend) insn_;
// {bs_private_append,{f,0},{integer,8},1,{y,0},{field_flags,[]},{x,1}}.
// * tmp_arg1 = Number of bytes to build
// * tmp_arg2 = Source binary
// * Operands: Fail Unit Dst
Arg extra_size = src_arg(insn_idx, insn.src2);
Arg src = src_arg(insn_idx, insn.src4);
int unit = insn.i3;
int flags = insn.i5;
Arg dst = dest_arg(insn_idx, insn.dest);
vis.visitBitStringAppend(opcode, decode_labelref(insn.label, type_map.exh), extra_size, src, unit, flags, dst);
break;
}
case bs_append: {
Insn.BSAppend insn = (Insn.BSAppend) insn_;
// {bs_append,{f,0},{integer,32},0,3,8,{x,1},{field_flags,[]},{x,0}}.
// * tmp_arg1 = Number of bytes to build
// * tmp_arg2 = Source binary
// * Operands: Fail ExtraHeap Live Unit Dst
Arg extra_size = src_arg(insn_idx, insn.src2);
Arg src = src_arg(insn_idx, insn.src6);
int unit = insn.i5;
int flags = insn.i7;
Arg dst = dest_arg(insn_idx, insn.dest8);
vis.visitBitStringAppend(opcode, decode_labelref(insn.label, type_map.exh), extra_size, src, unit, flags, dst);
break;
}
//default:
// throw new Error("unhandled insn: " + insn_.toSymbolicTuple());
}
}
}
private void do_call(BlockVisitor2 vis, int insn_idx, Insn.I insn,
boolean is_tail, boolean is_external) throws Error {
int arg_count = insn.i1;
Arg[] args = new Arg[arg_count];
for (int i = 0; i < arg_count; i++) {
args[i] = new Arg(Kind.X, i, this.map[insn_idx].getx(i));
}
ExtFun fun;
if (insn instanceof Insn.IE) {
fun = ((Insn.IE)insn).ext_fun;
} else if (insn instanceof Insn.IL) {
fun = ((Insn.IL)insn).functionAtLabel.asExtFun();
} else
throw new Error("unexpected in do_call: "+insn);
vis.visitCall(fun, args, is_tail, is_external);
}
private void accept_2_test(BlockVisitor2 vis, Insn.L insn_, int insn_idx) {
TypeMap typeMap = this.map[insn_idx];
int failLabel = decode_labelref(insn_.label, typeMap.exh);
BeamOpcode test = insn_.opcode();
if (insn_ instanceof Insn.LD) { // Handle simple type tests:
Insn.LD insn = (Insn.LD)insn_;
Arg arg = src_arg(insn_idx, insn.dest);
Type test_type = type_tested_for(insn);
if (test_type != null) {
if (insn_.opcode() == BeamOpcode.is_nonempty_list
|| !test_type.equals(arg.type))
vis.visitTest(test, failLabel, arg, test_type);
return;
}
}
switch (test) {
case is_function2: {
Insn.LDS insn = (Insn.LDS) insn_;
vis.visitTest(test, failLabel,
dest_arg(insn_idx, insn.dest),
src_arg(insn_idx,insn.src),
EFUN_TYPE);
break;
}
case is_eq_exact:
/* hack to convert types ... */
case is_lt:
case is_ge:
case is_ne_exact:
case is_ne:
case is_eq: {
Insn.LSS insn = (Insn.LSS) insn_;
Arg[] args = new Arg[] {
src_arg(insn_idx, insn.src1),
src_arg(insn_idx, insn.src2) };
Type outType = Type.VOID_TYPE;
Type t1 = getType(typeMap, insn.src1);
Type t2 = getType(typeMap, insn.src2);
if (t1.equals(EOBJECT_TYPE) && !t2.equals(EOBJECT_TYPE)) {
outType = t2;
}
if (t2.equals(EOBJECT_TYPE) && !t1.equals(EOBJECT_TYPE)) {
outType = t1;
}
vis.visitTest(test, failLabel, args, outType);
break;
}
case test_arity: {
Insn.LDI insn = (Insn.LDI) insn_;
int arity = insn.i;
Arg reg = src_arg(insn_idx, insn.dest);
vis.visitTest(test, failLabel, reg, arity,
getTupleType(arity));
break;
}
case bs_start_match2:
case bs_get_utf8:
case bs_get_utf16:
case bs_get_utf32: {
Insn.LDIID insn = (Insn.LDIID) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
insn.i4,
dest_arg(insn_idx, insn.dest5));
break;
}
case bs_match_string: {
Insn.LDBi insn = (Insn.LDBi) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
insn.bin.value);
break;
}
case bs_get_integer2:
case bs_get_float2:
case bs_get_binary2: {
Insn.LDISIID insn = (Insn.LDISIID) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
src_arg(insn_idx, insn.src4),
insn.i5,
insn.i6,
dest_arg(insn_idx, insn.dest7));
break;
}
case bs_skip_bits2: {
Insn.LDSII insn = (Insn.LDSII) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
src_arg(insn_idx, insn.src3),
insn.i4,
insn.i5);
break;
}
case bs_test_unit:
case bs_test_tail2: {
Insn.LDI insn = (Insn.LDI) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
insn.i);
break;
}
case bs_skip_utf8:
case bs_skip_utf16:
case bs_skip_utf32: {
Insn.LDII insn = (Insn.LDII) insn_;
vis.visitBitStringTest(test, failLabel,
src_arg(insn_idx, insn.dest),
insn.i4);
break;
}
default:
throw new Error("unhandled test: " + insn_.toSymbolic() +
" at index " + insn_idx +
" // " + test + ":" + test.ordinal());
}//switch
}
/**
* @param insn_idx
* @param array
* @return
*/
private Arg[] decode_args(int insn_idx, EObject[] input) {
Arg[] output = new Arg[input.length];
for (int i = 0; i < input.length; i++) {
output[i] = decode_arg(insn_idx, input[i]);
}
return output;
}
/**
* @param insnIdx
* @param eObject
* @return
*/
private Arg decode_arg(int insn_idx, EObject src) {
TypeMap current = this.map[insn_idx];
if (src instanceof ETuple2) {
ETuple2 tup = (ETuple2) src;
if (tup.elem1 == X_ATOM) {
int xreg = tup.elem2.asInt();
return new Arg(Arg.Kind.X, xreg, current.getx(xreg));
} else if (tup.elem1 == Y_ATOM) {
int yreg = tup.elem2.asInt();
return new Arg(Arg.Kind.Y, current.get_ypos(yreg),
current.gety(yreg));
} else if (tup.elem1 == FR_ATOM) {
int freg = tup.elem2.asInt();
return new Arg(Arg.Kind.F, freg, Type.DOUBLE_TYPE);
} else if (tup.elem1 == ATOM_ATOM) {
return new Arg(tup.elem2, EATOM_TYPE);
} else if (tup.elem1 == LITERAL_ATOM) {
return new Arg(tup.elem2);
} else if (tup.elem1 == STRING_ATOM) {
return new Arg(tup.elem2);
} else if (tup.elem1 == INTEGER_ATOM) {
return new Arg(tup.elem2);
} else if (tup.elem1 == FLOAT_ATOM) {
return new Arg(tup.elem2, Type.DOUBLE_TYPE);
}
} else if (src == NIL_ATOM) {
return new Arg(ERT.NIL, ENIL_TYPE);
}
return new Arg(src);
// return null;
}
private Arg[] src_args(int insn_idx, SourceOperand[] args) {
Arg[] res = new Arg[args.length];
for (int i=0; i<args.length; i++) {
res[i] = src_arg(insn_idx, args[i]);
}
return res;
}
private Arg src_arg(int insn_idx, SourceOperand src) {
TypeMap current = this.map[insn_idx];
if (src instanceof Operands.XReg)
return src_arg((Operands.XReg) src, current);
if (src instanceof Operands.YReg)
return src_arg((Operands.YReg) src, current);
if (src instanceof Operands.FReg)
return arg((Operands.FReg) src);
if (src instanceof Operands.Float)
return arg((Operands.Float) src);
if (src instanceof Operands.Literal)
return arg((Operands.Literal) src);
throw new Error("Unhandled src_arg: "+src.toSymbolic());
}
private Arg dest_arg(int insn_idx, DestinationOperand dest) {
TypeMap current = this.map[insn_idx];
if (dest instanceof Operands.XReg)
return dest_arg((Operands.XReg) dest);
if (dest instanceof Operands.YReg)
return dest_arg((Operands.YReg) dest, current);
if (dest instanceof Operands.FReg)
return arg((Operands.FReg) dest);
throw new Error("Unhandled dest_arg: "+dest.toSymbolic());
}
private Arg src_arg(Operands.XReg xreg, TypeMap tm) {
return new Arg(Kind.X, xreg.nr, tm.getx(xreg.nr));
}
private Arg src_arg(Operands.YReg yreg, TypeMap tm) {
return new Arg(Kind.Y, tm.get_ypos(yreg.nr), tm.gety(yreg.nr));
}
private Arg dest_arg(Operands.XReg xreg) {
return new Arg(Kind.X, xreg.nr);
}
private Arg dest_arg(Operands.YReg yreg, TypeMap tm) {
return new Arg(Kind.Y, tm.get_ypos(yreg.nr));
}
private Arg arg(Operands.FReg freg) {
return new Arg(Kind.F, freg.nr);
}
private Arg arg(Operands.Float flt) {
return new Arg(flt.literalValue(), Type.DOUBLE_TYPE);
}
private Arg arg(Operands.Literal lit) {
return new Arg(lit.literalValue());
}
/**
* @param insnIdx
* @param eObject
* @return
*/
private Arg decode_value(EObject src) {
if (src instanceof ETuple2) {
ETuple2 tup = (ETuple2) src;
if (tup.elem1 == ATOM_ATOM) {
return new Arg(tup.elem2, EATOM_TYPE);
} else if (tup.elem1 == LITERAL_ATOM) {
return new Arg(tup.elem2);
} else if (tup.elem1 == INTEGER_ATOM) {
return new Arg(tup.elem2);
} else if (tup.elem1 == FLOAT_ATOM) {
return new Arg(tup.elem2);
}
} else if (src == NIL_ATOM) {
return new Arg(ERT.NIL, ENIL_TYPE);
}
throw new Error("unknown value:" + src);
}
private Arg decode_out_arg(int insn_idx, EObject src) {
TypeMap current = this.map[insn_idx];
if (src instanceof ETuple2) {
ETuple2 tup = (ETuple2) src;
if (tup.elem1 == X_ATOM) {
int xreg = tup.elem2.asInt();
return new Arg(Arg.Kind.X, xreg);
} else if (tup.elem1 == Y_ATOM) {
int yreg = tup.elem2.asInt();
return new Arg(Arg.Kind.Y, current.get_ypos(yreg));
} else if (tup.elem1 == FR_ATOM) {
int freg = tup.elem2.asInt();
return new Arg(Arg.Kind.F, freg);
}
}
throw new Error();
}
/**
* @param nth
* @return
*/
private int decode_labelref(EObject f_tup, ExceptionHandler exh) {
if (f_tup == NOFAIL_ATOM)
return 0;
assert (f_tup.testTuple().elm(1) == F_ATOM);
return extendedLabel(f_tup.testTuple().elm(2).asInt(), exh);
}
private int decode_labelref(Operands.Label label, ExceptionHandler exh) {
if (label == null) return 0;
return extendedLabel(label.nr, exh);
}
public boolean isDeadCode() {
return initial == null;
}
public void analyze() {
try {
analyze0();
} catch (RuntimeException x) {
// dump();
throw x;
}
}
private void dump() {
if (!log.isLoggable(Level.FINER)) return;
next_insn: for (int i = 0; i < insns.size(); i++) {
log.finer(name + "(" + block_label + "):" + i
+ " :: " + (map == null ? "?" : map[i]));
log.finer(" >> " + insns.get(i));
}
}
public void analyze0() {
TypeMap current = initial;
BeamOpcode last_opcode = BeamOpcode.NONE;
Insn last_insn = null;
map = new TypeMap[insns.size()];
next_insn: for (int insn_idx = 0; insn_idx < insns.size(); insn_idx++) {
update_max_regs(current);
if (is_term(last_opcode)) {
throw new Error("how did we get here then...? "
+ this.block_label + ":" + insn_idx);
}
map[insn_idx] = current;
Insn insn_ = insns.get(insn_idx);
BeamOpcode code = insn_.opcode();
last_opcode = code; last_insn = insn_;
/*
* System.out.println(name + "(" + bb_label + "):" + i +
* " :: " + current + "" + insn);
*/
if (current.exh != null && may_terminate_exceptionally(code))
addExceptionEdge(current);
switch (code) {
case fmove:
case move: {
Insn.SD insn = (Insn.SD) insn_;
SourceOperand src = insn.src;
DestinationOperand dst = insn.dest;
Type srcType = getType(current, src);
// Determine type after possible conversion:
Type dstType = srcType;
if (dst.testFReg() != null) {
dstType = Type.DOUBLE_TYPE; // FRegs are always unboxed
} else if (sizeof(current, src) > sizeof(current, dst)) { // Conversion needed
if (srcType.equals(Type.DOUBLE_TYPE)) {
dstType = EDOUBLE_TYPE; // Box
} else {
throw new Error("why?" + insn.toSymbolic()
+ "; srcType="+getType(current,src));
}
}
current = setType(current, dst, dstType);
continue next_insn;
}
case put_string: {
Insn.ByD insn = (Insn.ByD) insn_;
DestinationOperand dst = insn.dest;
current = setType(current, dst, ESEQ_TYPE);
continue next_insn;
}
case jump: {
Insn.L insn = (Insn.L) insn_;
current = branch(current, insn.label, insn_idx);
continue next_insn;
}
case send: {
current.touchx(0, 2);
current = current.setx(0, current.getx(1), FV.this);
continue next_insn;
}
case fnegate: {
Insn.LSD insn = (Insn.LSD)insn_;
EAtom name = insn.opcode().symbol;
SourceOperand[] parms = new SourceOperand[] {
insn.src
};
Type type = getBifResult("erlang", name.getName(),
parmTypes(current, parms), false);
current = setType(current, insn.dest, type);
continue next_insn;
}
case fadd:
case fsub:
case fmul:
case fdiv:
{
Insn.LSSD insn = (Insn.LSSD) insn_;
EAtom name = insn.opcode().symbol;
SourceOperand[] parms = new SourceOperand[] {
insn.src1, insn.src2
};
Type type = getBifResult("erlang", name.getName(),
parmTypes(current, parms), false);
current = setType(current, insn.dest, type);
continue next_insn;
}
case gc_bif1:
case gc_bif2:
case gc_bif3:
{
// {gc_bif,BifName,F,Live,[A1,A2?],Reg};
Insn.GcBif insn = (Insn.GcBif) insn_;
boolean is_guard = (insn.label.nr != 0);
current = branch(current, insn.label, insn_idx);
EAtom name = insn.ext_fun.fun;
SourceOperand[] parms = insn.argList();
Type type = getBifResult("erlang", name.getName(),
parmTypes(current, parms), is_guard);
current = setType(current, insn.dest, type);
continue next_insn;
}
case bif0:
case bif1:
case bif2:
{
Insn.Bif insn = (Insn.Bif) insn_;
current = branch(current, insn.label, insn_idx);
EAtom name = insn.ext_fun.fun;
SourceOperand[] parms = insn.argList();
Type type = getBifResult("erlang", name.getName(),
parmTypes(current, parms), false);
current = setType(current, insn.dest, type);
continue next_insn;
}
case is_tuple: {
if (insn_idx+1 < insns.size()) {
Insn next_insn = insns.get(insn_idx+1);
if (next_insn.opcode() == BeamOpcode.test_arity) {
if (this.map[insn_idx+1] == null) {
this.map[insn_idx+1] = this.map[insn_idx];
}
int this_fail = decode_labelref(((Insn.L)insn_).label, this.map[insn_idx].exh);
int next_fail = decode_labelref(((Insn.L)next_insn).label, this.map[insn_idx+1].exh);
if (this_fail == next_fail) {
Arg this_arg = src_arg(insn_idx, ((Insn.LD)insn_).dest);
Arg next_arg = src_arg(insn_idx+1, ((Insn.LD)next_insn).dest);
if (this_arg.equals(next_arg)) {
// SKIP THIS INSTRUCTION!
continue next_insn;
}
}
}
}
}
// Tests:
// LS:
case is_integer:
case is_float:
case is_number:
case is_atom:
case is_pid:
case is_reference:
case is_port:
case is_nil:
case is_binary:
case is_list:
case is_nonempty_list:
case is_function:
case is_boolean:
case is_bitstr:
// LSI:
case test_arity:
case bs_test_tail2:
case bs_test_unit:
// LSS:
case is_lt:
case is_ge:
case is_eq:
case is_ne:
case is_eq_exact:
case is_ne_exact:
case is_function2:
// LSBi:
case bs_match_string:
// LSII:
case bs_skip_utf8:
case bs_skip_utf16:
case bs_skip_utf32:
// LSIID:
case bs_start_match2:
case bs_get_utf8:
case bs_get_utf16:
case bs_get_utf32:
// LSSII:
case bs_skip_bits2:
// LSISIID:
case bs_get_integer2:
case bs_get_float2:
case bs_get_binary2:
{
try {
current = analyze_test(current, (Insn.L)insn_, insn_idx);
} catch (Error e) {
throw new Error(" at "
+ LabeledBlock.this.block_label + ":"
+ insn_idx, e);
}
assert (current != null);
continue next_insn;
}
case fconv: {
// unbox and convert to DOUBLE
Insn.SD insn = (Insn.SD) insn_;
getType(current, insn.src);
current = setType(current, insn.dest,
Type.DOUBLE_TYPE);
continue next_insn;
}
case init: {
Insn.D insn = (Insn.D) insn_;
current = setType(current, insn.dest, ENIL_TYPE);
continue next_insn;
}
case set_tuple_element: {
Insn.SDI insn = (Insn.SDI) insn_;
getType(current, insn.src);
getType(current, insn.dest);
continue next_insn;
}
case get_tuple_element: {
Insn.SID insn = (Insn.SID) insn_;
getType(current, insn.src);
current = setType(current, insn.dest, EOBJECT_TYPE);
continue next_insn;
}
case get_list: {
Insn.SDD insn = (Insn.SDD) insn_;
current = setType(current, insn.dest1, EOBJECT_TYPE);
Type list_type = getType(current, insn.src);
Type tail_type =
(list_type == ELIST_TYPE || list_type == ESEQ_TYPE)
? ESEQ_TYPE : EOBJECT_TYPE;
current = setType(current, insn.dest2, tail_type);
continue next_insn;
}
case put_list: {
Insn.SSD insn = (Insn.SSD) insn_;
Type head_type = getType(current, insn.src1);
Type tail_type = getType(current, insn.src2);
if (tail_type == null) {
throw new Error("value: " + insn.src2.toSymbolic()
+ " has no type");
}
Type list_type = (tail_type.equals(ENIL_TYPE)
|| tail_type.equals(ESEQ_TYPE)
|| tail_type.equals(ELIST_TYPE))
? ELIST_TYPE : ECONS_TYPE;
current = setType(current, insn.dest, list_type);
continue next_insn;
}
case put_tuple: {
Insn.ID insn = (Insn.ID) insn_;
int arity = insn.i1;
current = setType(current, insn.dest, getTupleType(arity));
continue next_insn;
}
case K_try: {
Insn.YL insn = (Insn.YL) insn_;
current = setType(current, insn.y, EOBJECT_TYPE);
current = installExceptionHandler(current, insn.label, insn_idx);
continue next_insn;
}
case try_end: {
current = current.popExceptionHandler();
continue next_insn;
}
case try_case: {
Insn.Y insn = (Insn.Y) insn_;
getType(current, insn.y);
current = current.popExceptionHandler();
current = current.setx(0, EATOM_TYPE, FV.this); // exc.class
current = current.setx(1, EOBJECT_TYPE, FV.this); // value/reason
current = current.setx(2, EOBJECT_TYPE, FV.this); // exc.object
continue next_insn;
}
case try_case_end:
continue next_insn;
case raise: {
Insn.SS insn = (Insn.SS) insn_;
checkArg(current, insn.src1);
checkArg(current, insn.src2);
current = setType(current, Operands.XReg.get(0), EOBJECT_TYPE);
continue next_insn;
}
case K_catch: {
Insn.YL insn = (Insn.YL) insn_;
current = installExceptionHandler(current, insn.label, insn_idx);
continue next_insn;
}
case catch_end: {
current = current.popExceptionHandler();
current = current.setx(0, EOBJECT_TYPE, FV.this); // value
continue next_insn;
}
case make_fun2: {
Insn.F insn = (Insn.F) insn_;
current.touchx(0, insn.anon_fun.free_vars);
current = current.setx(0, EFUN_TYPE, FV.this);
continue next_insn;
}
case loop_rec: {
Insn.LD insn = (Insn.LD) insn_;
current = branch(current, insn.label, insn_idx);
current = setType(current, insn.dest, EOBJECT_TYPE);
continue next_insn;
}
case remove_message:
// assume this insn overrides X0
// current = current.setx(0, EOBJECT_TYPE, FV.this);
continue next_insn;
case loop_rec_end:
case timeout: {
// log.finer(insn);
continue next_insn;
}
case wait_timeout: {
checkArg(current, ((Insn.LS)insn_).src);
} // fall-through
case wait: {
Insn.L insn = (Insn.L) insn_;
current = branch(current, insn.label, insn_idx);
continue next_insn;
}
case deallocate:
case trim: {
Insn.I insn = (Insn.I) insn_;
int howmuch = insn.i1;
current = current.trim_y(howmuch);
continue next_insn;
}
// this is really a no-op in jave
case on_load:
case test_heap: {
continue next_insn;
}
case allocate_zero:
case allocate_heap_zero: {
Insn.I insn = (Insn.I) insn_;
int slots = insn.i1;
current = current.alloc_y(slots);
for (int slot = 0; slot < slots; slot++) {
current = current.sety(slot, ENIL_TYPE);
}
continue next_insn;
}
case allocate:
case allocate_heap: {
Insn.I insn = (Insn.I) insn_;
current = current.alloc_y(insn.i1);
continue next_insn;
}
case fcheckerror:
case fclearerror:
continue next_insn;
case recv_mark:
case recv_set:
continue next_insn;
case put: {
Insn.S insn = (Insn.S) insn_;
checkArg(current, insn.src);
continue next_insn;
}
case select_tuple_arity: {
Insn.Select insn = (Insn.Select) insn_;
current = branch(current, insn.defaultLabel, insn_idx);
checkArg(current, insn.src);
DestinationOperand dest = insn.src.testDestination();
Operands.SelectList jumpTable = insn.jumpTable;
int len = jumpTable.size();
for (int i=0; i<len; i++) {
Operands.Operand value = jumpTable.getValue(i);
Operands.Label target = jumpTable.getLabel(i);
if (dest != null) {
int arity = value.asCodeInt().value;
current = setType(current, dest, getTupleType(arity));
}
current = branch(current, target, insn_idx);
}
continue next_insn;
}
case select_val: {
Insn.Select insn = (Insn.Select) insn_;
current = branch(current, insn.defaultLabel, insn_idx);
checkArg(current, insn.src);
Operands.SelectList jumpTable = insn.jumpTable;
int len = jumpTable.size();
for (int i=0; i<len; i++) {
Operands.Label target = jumpTable.getLabel(i);
//TODO: Set the known type of 'src'
current = branch(current, target, insn_idx);
}
continue next_insn;
}
// we loose the type of the result
case apply:
case call:
case call_ext: {
Insn.I insn = (Insn.I) insn_;
int argCount = insn.i1;
current.touchx(0, argCount);
current = current.setx(0, EOBJECT_TYPE, FV.this);
continue next_insn;
}
// all these exit
case K_return:
getType(current, X0_REG);
continue next_insn;
case apply_last:
case call_last:
case call_only:
case call_ext_last:
case call_ext_only: {
Insn.I insn = (Insn.I) insn_;
int argCount = insn.i1;
current.touchx(0, argCount);
is_tail_recursive = true;
continue next_insn;
}
case func_info:
continue next_insn;
case if_end:
case badmatch:
case case_end:
continue next_insn;
case bs_add: {
Insn.LSSID insn = (Insn.LSSID) insn_;
checkArg(current, insn.src1);
checkArg(current, insn.src2);
current = setType(current, insn.dest, Type.INT_TYPE);
continue next_insn;
}
case bs_context_to_binary: {
Insn.D insn = (Insn.D) insn_;
checkArg(current, insn.dest);
current = current.setx(0, EBINARY_TYPE, FV.this);
continue next_insn;
}
case bs_save2: {
continue next_insn;
}
case bs_restore2: {
Insn.DI insn = (Insn.DI) insn_;
current = setType(current, insn.dest, EMATCHSTATE_TYPE);
continue next_insn;
}
case bs_init2: {
Insn.LSIIID insn = (Insn.LSIIID) insn_;
// int size = insn.elm(3).asInt();
current = setType(current, insn.dest, EBINARY_TYPE);
continue next_insn;
}
case bs_init_bits: {
Insn.LSIIID insn = (Insn.LSIIID) insn_;
// int size = insn.elm(3).asInt();
current = setType(current, insn.dest, EBITSTRING_TYPE);
continue next_insn;
}
case bs_init_writable: {
XReg x0 = new Operands.XReg(0);
checkArg(current, x0);
current = setType(current, x0, EBITSTRING_TYPE);
continue next_insn;
}
case bs_private_append: {
Insn.BSPrivateAppend insn = (Insn.BSPrivateAppend)insn_;
checkArg(current, insn.src2);
checkArg(current, insn.src4);
current = setType(current, insn.dest, EBITSTRING_TYPE);
continue next_insn;
}
case bs_append: {
// {bs_append,{f,0},{integer,32},0,3,8,{x,1},{field_flags,[]},{x,0}}.
Insn.BSAppend insn = (Insn.BSAppend)insn_;
// Arg extra_size = decode_arg(insn_idx, insn.elm(3));
// Arg src = decode_arg(insn_idx, insn.elm(7));
// Arg flags = decode_arg(insn_idx, insn.elm(8));
// Arg dst = decode_out_arg(insn_idx, insn.elm(9));
checkArg(current, insn.src6);
current = setType(current, insn.dest8, EBITSTRING_TYPE);
continue next_insn;
}
case bs_put_string:
case bs_put_binary:
case bs_put_float:
case bs_put_integer: {
continue next_insn;
}
case bs_put_utf8:
case bs_put_utf16:
case bs_put_utf32: {
Insn.LIS insn = (Insn.LIS)insn_;
// make sure there is a source
checkArg(current, insn.src);
continue next_insn;
}
case bs_utf8_size:
case bs_utf16_size: {
// {bs_utf16_size,{f,0},src={x,0},dst={x,2}}
Insn.LSD insn = (Insn.LSD)insn_;
checkArg(current, insn.src);
current = setType(current, insn.dest, ESMALL_TYPE);
continue next_insn;
}
case call_fun:
case i_call_fun_last: {
Insn.I insn = (Insn.I)insn_;
int nargs = insn.i1;
for (int i = 0; i < nargs; i++) {
if (current.getx(i) == null)
throw new Error("uninitialized x" + i);
}
if (code == BeamOpcode.i_call_fun_last) {
is_tail_recursive = true;
} else {
current = current.setx(0, EOBJECT_TYPE, FV.this);
}
continue next_insn;
}
case line: {
// TODO: Implement LINE instruction!
continue next_insn;
}
default: {
ETuple insn = insn_.toSymbolicTuple();
throw new Error("unhandled: " + insn + "::" + current);
}
}//switch
}
update_max_regs(current);
if (is_term(last_opcode) == false &&
!is_exceptional_call(last_insn))
{
LabeledBlock lbv;
lbv = get_lb(this.block_label + 1, false);
try {
if (lbv != null)
lbv.merge_from(current);
} catch (Error e) {
log.severe("merge " + current + "\n | "
+ lbv.initial + "\n FAILED");
throw e;
}
}
}
boolean is_term(BeamOpcode code) {
switch (code) {
case K_return:
case if_end:
case badmatch:
case case_end:
case try_case_end:
case call_last:
case call_only:
case call_ext_last:
case call_ext_only:
case func_info:
case apply_last:
case i_call_fun_last:
case wait:
case select_tuple_arity:
case select_val:
case jump:
return true;
default:
return false;
}
}
boolean may_terminate_exceptionally(BeamOpcode code) {
// A conservative approximation:
switch (code) {
case label:
case jump:
case K_return:
case move:
case K_try:
case K_catch:
case try_end:
case catch_end:
case line:
case try_case:
case is_eq_exact:
return false;
case case_end:
default:
return true;
}
}
boolean is_exceptional_call(Insn insn) {
BeamOpcode opcode = insn.opcode();
if (opcode == BeamOpcode.call_ext) {
Insn.IE spec_insn = (Insn.IE)insn;
ExtFun ext_fun = spec_insn.ext_fun;
if (ext_fun.mod == ERLANG_ATOM &&
(ext_fun.fun == ERROR_ATOM
|| ext_fun.fun == THROW_ATOM
|| ext_fun.fun == EXIT_ATOM
|| ext_fun.fun == NIF_ERROR_ATOM) &&
ext_fun.arity == 1) return true;
if (ext_fun.mod == ERLANG_ATOM &&
ext_fun.fun == ERROR_ATOM &&
ext_fun.arity == 2) return true;
}
return false;
}
private int sizeof(TypeMap current, Operands.SourceOperand cell) {
if (cell instanceof Operands.XReg ||
cell instanceof Operands.YReg)
return 32;
if (cell instanceof Operands.FReg)
return 64;
Type t = getType(current, cell);
if (t == Type.DOUBLE_TYPE) {
return 64;
} else {
return 32;
}
}
private Type getBifResult(String module, String name, Type[] parmTypes,
boolean is_guard) {
return BIFUtil.getBifResult(module, name, parmTypes, is_guard);
}
@Deprecated
private Type[] parmTypes(TypeMap current, ESeq args) {
ArrayList<Type> res = new ArrayList<Type>();
while (args != ERT.NIL) {
EObject arg = args.head();
res.add(getType(current, arg));
args = args.tail();
}
return res.toArray(new Type[res.size()]);
}
private Type[] parmTypes(TypeMap current, SourceOperand[] args) {
Type[] res = new Type[args.length];
for (int i=0; i<args.length; i++) {
SourceOperand arg = args[i];
Type argType = getType(current, arg);
if (argType == null) {
throw new Error("uninitialized " + arg);
}
res[i] = argType;
}
return res;
}
private void checkArg(TypeMap current, SourceOperand arg) {
Type argType = getType(current, arg);
if (argType == null) {
throw new Error("uninitialized " + arg);
}
}
private TypeMap analyze_test(TypeMap current, Insn.L insn_, int insn_idx) {
current = branch(current, insn_.label, insn_idx);
BeamOpcode opcode = insn_.opcode();
switch (opcode) {
case is_lt:
case is_ge:
case is_ne:
case is_eq:
case is_ne_exact: {
Insn.LSS insn = (Insn.LSS) insn_;
checkArg(current, insn.src1);
checkArg(current, insn.src2);
return current;
}
case is_eq_exact: {
Insn.LSS insn = (Insn.LSS) insn_;
checkArg(current, insn.src1);
checkArg(current, insn.src2);
Type t1 = getType(current, insn.src1);
Type t2 = getType(current, insn.src2);
if (!t1.equals(t2)) {
//TODO: for reg-vs-reg, we should really use the GLB.
DestinationOperand reg;
if ((reg = insn.src1.testDestination()) != null) {
current = setType(current, reg, t2);
}
if ((reg = insn.src2.testDestination()) != null) {
current = setType(current, reg, t1);
}
}
return current;
}
case bs_start_match2: {
Insn.LDIID insn = (Insn.LDIID) insn_;
checkArg(current, insn.dest);
return setType(current, insn.dest5, EMATCHSTATE_TYPE);
}
case bs_get_integer2: {
Insn.LDISIID insn = (Insn.LDISIID) insn_;
if (!EMATCHSTATE_TYPE.equals(getType(current, insn.dest))) {
throw new Error("matching without a state");
}
Int bits = insn.src4.testInt();
int unit = insn.i5;
int flags = insn.i6;
if (unit == 1 && flags == 0 && bits != null && (bits.value*unit) <= 32 ) {
return setType(current, insn.dest7, ESMALL_TYPE);
}
/* DISABLED because it triggers a get_test_bif()-related bug
if (insn.i5 <= 32) {
return setType(current, insn.dest7, Type.INT_TYPE);
}
*/
return setType(current, insn.dest7, EINTEGER_TYPE);
}
case bs_get_binary2: {
Insn.LDISIID insn = (Insn.LDISIID) insn_;
if (!EMATCHSTATE_TYPE.equals(getType(current, insn.dest))) {
throw new Error("matching without a state");
}
return setType(current, insn.dest7, EBINARY_TYPE);
}
case bs_get_float2: {
Insn.LDISIID insn = (Insn.LDISIID) insn_;
if (!EMATCHSTATE_TYPE.equals(getType(current, insn.dest))) {
throw new Error("matching without a state");
}
return setType(current, insn.dest7, Type.DOUBLE_TYPE);
}
case bs_test_tail2:
case bs_test_unit:
case bs_skip_bits2:
case bs_match_string:
case bs_skip_utf8:
case bs_skip_utf16:
case bs_skip_utf32: {
// These bit string matchers don't modify registers.
Insn.LD insn = (Insn.LD) insn_;
if (!EMATCHSTATE_TYPE.equals(getType(current, insn.dest))) {
throw new Error("matching without a state");
}
return current;
}
case bs_get_utf8:
case bs_get_utf16:
case bs_get_utf32: {
// These bit string matchers don't modify registers.
Insn.LDIID insn = (Insn.LDIID) insn_;
if (!EMATCHSTATE_TYPE.equals(getType(current, insn.dest))) {
throw new Error("matching without a state");
}
return setType(current, insn.dest5, ESMALL_TYPE);
}
case is_integer:
{
Insn.LD insn = (Insn.LD) insn_;
checkArg(current, insn.dest);
if (getType(current, insn.dest) == Type.INT_TYPE
|| getType(current, insn.dest).equals(ESMALL_TYPE) ) {
return setType(current, insn.dest, ESMALL_TYPE);
}
}
default: { // All type tests:
Insn.LD insn = (Insn.LD) insn_;
checkArg(current, insn.dest);
switch (opcode) {
case test_arity: {
int arity = ((Insn.LDI)insn).i;
return setType(current, insn.dest, getTupleType(arity));
}
case is_function2: {
Insn.LDS insn2 = (Insn.LDS) insn;
checkArg(current, insn2.src);
// TODO: Use more specific type when arity is known?
return setType(current, insn.dest, EFUN_TYPE);
}
default: {
if (insn instanceof Insn.LD) {
Type test_type = type_tested_for((Insn.LD)insn);
if (test_type != null)
return setType(current, insn.dest, test_type);
}
throw new Error("unhandled test: " + insn_.toSymbolic());
}
}//switch
}
}//switch
}
private Type type_tested_for(Insn.LD insn) {
switch (insn.opcode()) {
case is_nil: return ENIL_TYPE;
case is_binary: return EBINARY_TYPE;
case is_tuple: return ETUPLE_TYPE;
case is_integer: return EINTEGER_TYPE;
case is_bitstr: return EBITSTRING_TYPE;
case is_number: return ENUMBER_TYPE;
case is_pid: return EPID_TYPE;
case is_port: return EPORT_TYPE;
case is_reference: return EREFERENCE_TYPE;
case is_float: return EDOUBLE_TYPE;
case is_function: return EFUN_TYPE;
case is_list:
case is_nonempty_list:
return ECONS_TYPE;
case is_boolean:
case is_atom:
return EATOM_TYPE;
default:
return null;
}//switch
}
private TypeMap branch(TypeMap current, Operands.Label target, int idx) {
return branch(current, target==null? -1 : target.nr, idx);
}
private TypeMap branch(TypeMap current, int target, int idx) {
if (target > 0) {
get_lb(target, false).merge_from(current);
}
return current.clearLive(makeBasicBlock(block_label, idx + 1));
}
private TypeMap installExceptionHandler(TypeMap current, Operands.Label target, int idx) {
TypeMap afterPush = current.pushExceptionHandler(target.nr);
return afterPush.clearLive(makeBasicBlock(block_label, idx + 1));
}
private void addExceptionEdge(TypeMap current) {
int handler_lbl = current.exh.getHandlerLabel();
if (handler_lbl>=0) get_lb(handler_lbl, false).merge_from(current);
}
private Type getTupleType(int arity) {
ETuple.get_tuple_class(arity);
String tt = "L" + ETUPLE_TYPE.getInternalName() + arity + ";";
return Type.getType(tt);
}
public void merge_from(TypeMap typeMap) {
if (initial == null) {
initial = typeMap.clearLive(makeBasicBlock(
this.block_label, 0));
needs_analyze.add(this);
} else {
TypeMap new_types = initial.mergeFrom(typeMap);
if (new_types == initial) {
// ignore //
} else if (new_types.equals(initial)) {
if (log.isLoggable(Level.FINE)) log.fine("Missed TypeMap sharing opportunity");
} else {
// System.out.println("merge " + initial + "\n | " +
// typeMap + "\n -> " + new_types);
initial = new_types;
needs_analyze.add(this);
}
}
typeMap.add_succ(initial.bb);
}
List<Insn> insns = new ArrayList<Insn>();
@Override
public void visitEnd() {
}
@Override
public void visitInsn(Insn insn) {
insns.add(insn);
}
private TypeMap setType(TypeMap current, EObject dd, Type type) {
ETuple dst = dd.testTuple();
EObject key = dst.elm(1);
EObject value = dst.elm(2);
if (key == X_ATOM) {
current = current.setx(value.asInt(),
type == Type.DOUBLE_TYPE ? EDOUBLE_TYPE : type,
FV.this);
} else if (key == Y_ATOM) {
current = current.sety(value.asInt(),
type == Type.DOUBLE_TYPE ? EDOUBLE_TYPE : type);
} else if (key == FR_ATOM) {
current = current.setf(value.asInt(), type);
} else {
throw new Error("unknown " + dst);
}
return current;
}
private TypeMap setType(TypeMap current, DestinationOperand dst, Type type) {
{
Operands.FReg freg;
if ((freg = dst.testFReg()) != null) {
return current.setf(freg.nr, type);
}
}
type = type == Type.DOUBLE_TYPE ? EDOUBLE_TYPE : type;
{
Operands.XReg xreg;
if ((xreg = dst.testXReg()) != null) {
return current.setx(xreg.nr, type, FV.this);
}
}
{
Operands.YReg yreg;
if ((yreg = dst.testYReg()) != null) {
return current.sety(yreg.nr, type);
}
}
throw new Error("unknown " + dst);
}
@Deprecated
private Type getType(TypeMap current, EObject src) {
if (src instanceof ETuple2) {
ETuple2 tup = (ETuple2) src;
if (tup.elem1 == X_ATOM) {
return current.getx(tup.elem2.asInt());
} else if (tup.elem1 == Y_ATOM) {
return current.gety(tup.elem2.asInt());
} else if (tup.elem1 == FR_ATOM) {
return current.getf(tup.elem2.asInt());
} else if (tup.elem1 == ATOM_ATOM) {
return EATOM_TYPE;
} else if (tup.elem1 == LITERAL_ATOM) {
return Type.getType(tup.elem2.getClass());
} else if (tup.elem1 == INTEGER_ATOM) {
if (tup.elem2.getClass() == ESmall.class) {
return ESMALL_TYPE;
} else if (tup.elem2.getClass() == EBig.class) {
return EBIG_TYPE;
} else {
return Type.getType(tup.elem2.getClass());
}
} else if (tup.elem1 == FLOAT_ATOM) {
return Type.DOUBLE_TYPE;
} else if (tup.elem1 == FIELD_FLAGS_ATOM) {
return Type.INT_TYPE;
}
} else if (src == NIL_ATOM) {
return ENIL_TYPE;
} else if (src instanceof ESmall) {
return EINTEGER_TYPE;
}
throw new Error("unknown " + src);
}
private Type getType(TypeMap current, SourceOperand src) {
{
Operands.XReg xreg;
if ((xreg = src.testXReg()) != null)
return current.getx(xreg.nr);
}
{
Operands.YReg yreg;
if ((yreg = src.testYReg()) != null)
return current.gety(yreg.nr);
}
{
Operands.FReg freg;
if ((freg = src.testFReg()) != null)
return current.getf(freg.nr);
}
if ((src.testAtom()) != null)
return EATOM_TYPE;
else if ((src.testInt()) != null)
return ESMALL_TYPE;
else if ((src.testBigInt()) != null)
return EBIG_TYPE;
else if ((src.testFloat()) != null)
return Type.DOUBLE_TYPE;
else if (src instanceof Operands.Nil)
return ENIL_TYPE;
{
Operands.TableLiteral lit;
if ((lit = src.testTableLiteral()) != null)
return Type.getType(lit.value.getClass());
}
throw new Error("unknown " + src);
}
@Override
public BeamInstruction[] getInstructions() {
BeamInstruction[] res = new BeamInstruction[insns.size()];
for (int i = 0; i < insns.size(); i++) {
res[i] = new BInsn(insns.get(i), this.map[i]);
}
return res;
}
@Override
public int getLabel() {
return this.block_label;
}
class BInsn implements BeamInstruction {
private final Insn insn;
private final TypeMap current;
public BInsn(Insn insn, TypeMap current) {
this.insn = insn;
this.current = current;
}
@Override
public BeamOpcode opcode() {
return insn.opcode();
}
@Override
public String toString() {
return insn.toString();
}
}
}
@Override
public int getArity() {
return arity;
}
@Override
public String getName() {
return name.getName();
}
@Override
public String getModuleName() {
return moduleName.getName();
}
@Override
public boolean isExported() {
String externalName = getName() + "/" + getArity();
return exports.contains(externalName);
}
@Override
public int getFregCount() {
return max_freg;
}
@Override
public Set<Integer> getXRegisters() {
return all_xregs;
}
@Override
public int getYregCount() {
return max_stack;
}
@Override
public BeamCodeBlock[] getCodeBlocks() {
BeamCodeBlock[] blocks = this.lbs.values().toArray(
new BeamCodeBlock[0]);
return blocks;
}
@Override
public int getEntryLabel() {
return startLabel;
}
}
public void visitModule(EAtom name) {
this.moduleName = name;
super.visitModule(name);
}
Set<String> exports = new HashSet<String>();
/** list of {Fun,Arity,Entry} */
public void visitExport(EAtom fun, int arity, int entry) {
exports.add(fun.getName() + "/" + arity);
super.visitExport(fun, arity, entry);
}
/** list of {Atom,Value} */
public void visitAttribute(EAtom att, EObject value) {
super.visitAttribute(att, value);
}
public String getModuleName() {
return this.moduleName.getName();
}
public BeamFunction[] functions() {
return functions.toArray(new BeamFunction[functions.size()]);
}
}