package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.BAOS; import com.seaofnodes.simple.util.Utils; import java.util.HashMap; import java.util.IdentityHashMap; public abstract class ArithNode extends Node implements ModeNode { // Source location for late reported errors Parser.Lexer _loc; // Mode is 0 for unknown, 1 for int, 2 for flt byte _mode; public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; } public ArithNode( Parser.Lexer loc, Node lhs, Node rhs, byte mode) { this(loc,lhs,rhs); _mode=mode; } @Override public void packed(BAOS baos, HashMap strs, HashMap types, IdentityHashMap anodes) { baos.packed1(_mode); } abstract String op(); @Override protected ExprPrinter _print1(ExprPrinter p) { return p.open().n(in(1)).p(op()).n(in(2)).close(); } abstract long doOp(long x, long y); abstract TypeInteger doOp(TypeInteger x, TypeInteger y); double doOp(double x, double y) { throw Utils.TODO(); } @Override public byte mode() { return _mode; } @Override public Node setMode(byte mode) { assert _mode==0 && (mode==1 || mode==2); unlock(); // _mode participates in GVN hash and equality _mode = mode; return this; } static byte mode( Type t1, Type t2 ) { if( t1 instanceof TypeFloat || t2 instanceof TypeFloat ) return 2; if( t1 instanceof TypeInteger && t2 instanceof TypeInteger ) return 1; if( t1 instanceof TypeMemPtr && t2 instanceof TypeMemPtr ) return 1; if( t1 instanceof TypeFunPtr && t2 instanceof TypeFunPtr ) return 1; return 0; } // Int-only ops, such as &, | and shifts, can collapse to one input. If // that input, or the live arms of a Phi input, are still mode-polymorphic // ops, settle them before returning the value so the replacement remains // at least as narrow as the int-only op. static boolean forceInt(Node n) { if( n instanceof ModeNode mode && mode.mode()==0 ) { mode.setMode((byte)1); n.setType(n.compute()); } return true; } // Check that a settled mode agrees with all data inputs. Takes the Node // instead of a varargs list both to avoid allocation and to keep this ready // to become shared mode-aware Node behavior later. static Parser.ParseException nodeErr(Node n, Parser.Lexer loc, String op, byte mode, boolean allowFloat) { Type bad = null; for( int i=1; i mode==1 ? null : t; case TypeMemPtr ti -> mode==1 ? null : t; case TypeFunPtr ti -> mode==1 ? null : t; case TypeFloat tf -> mode==2 ? null : t; default -> t; }; if( bad != null ) break; } boolean unsupportedFloat = false; if( !allowFloat ) for( int i=1; i= 2 && !lhs.inProgress() && // Disallow with self-looping phi; these will collapse (lhs.nIns() > 2 && lhs.in(2)!=lhs && rhs.nIns() > 2 && rhs.in(2)!=rhs) ) { Node r = lhs.region(); if( r==rhs.region() && !(r instanceof FunNode) && // For loops, the backedge *must* fold away, lest we unroll forever (!(r instanceof LoopNode) || isCon(lhs,rhs,2)) ) { // Profit check: only 1 instance of `this` will remain, all the // others will fold to constants. For loops, *all* must fold away int cnt=0; for( int i=1; i