From: megacz Date: Fri, 13 Mar 2009 16:04:54 +0000 (-0700) Subject: move LoopFactory into a separate class X-Git-Url: http://git.megacz.com/?a=commitdiff_plain;h=faaac5900895dc34540013f0b36580b3f700ad78;p=fleet.git move LoopFactory into a separate class --- diff --git a/src/edu/berkeley/fleet/ir/Counter.java b/src/edu/berkeley/fleet/ir/Counter.java index 1bd1920..059f5a6 100644 --- a/src/edu/berkeley/fleet/ir/Counter.java +++ b/src/edu/berkeley/fleet/ir/Counter.java @@ -9,7 +9,6 @@ import edu.berkeley.fleet.api.*; import edu.berkeley.fleet.api.Instruction.*; import edu.berkeley.fleet.api.Instruction.Set; import edu.berkeley.fleet.api.Instruction.Set.*; -import edu.berkeley.fleet.loops.Context.LoopFactory; import static edu.berkeley.fleet.util.BitManipulations.*; import edu.berkeley.fleet.api.Instruction.Set.FlagFunction; import edu.berkeley.fleet.api.Instruction.Set; @@ -36,9 +35,9 @@ public class Counter { Dock in1 = alu.getDock("in1"); Dock in2 = alu.getDock("in2"); - LoopFactory lf_in1 = ctx.new LoopFactory(in1, 1); - LoopFactory lf_in2 = ctx.new LoopFactory(in2, 1); - LoopFactory lf_inOp = ctx.new LoopFactory(inOp, 1); + LoopFactory lf_in1 = new LoopFactory(ctx, in1, 1); + LoopFactory lf_in2 = new LoopFactory(ctx, in2, 1); + LoopFactory lf_inOp = new LoopFactory(ctx, inOp, 1); for(int i=0; i il) { } public void emitInstructions(ArrayList il) { LoopFactory lf; - lf = context.new LoopFactory(debugShip.getDock("in"), 0, "debug.in"); + lf = new LoopFactory(context, debugShip.getDock("in"), 0, "debug.in"); lf.recvWord(); lf.deliver(); } diff --git a/src/edu/berkeley/fleet/ir/Process.java b/src/edu/berkeley/fleet/ir/Process.java index ce82b5a..8f48fef 100644 --- a/src/edu/berkeley/fleet/ir/Process.java +++ b/src/edu/berkeley/fleet/ir/Process.java @@ -70,7 +70,7 @@ public class Process { case 0: { if (!dock.isInputDock()) { torpedoes.add(dock); - Context.LoopFactory lf = ctx.new LoopFactory(dock, 1); + LoopFactory lf = new LoopFactory(ctx, dock, 1); lf.sendToken(ackDestination); lf = lf.makeNext(0); lf.abortLoopIfTorpedoPresent(); @@ -84,7 +84,7 @@ public class Process { case 1: { if (dock.isInputDock()) { torpedoes.add(dock); - Context.LoopFactory lf = ctx.new LoopFactory(dock, 1); + LoopFactory lf = new LoopFactory(ctx, dock, 1); lf.sendToken(ackDestination); // FIXME: this won't work right for ports that @@ -106,7 +106,7 @@ public class Process { case 2: { if (!dock.isInputDock()) { torpedoes.add(dock); - Context.LoopFactory lf = ctx.new LoopFactory(dock, 1); + LoopFactory lf = new LoopFactory(ctx, dock, 1); if (peer != null) for(int i=0; i<((InPort)peer).getTokensToAbsorb(); i++) lf.recvToken(); @@ -122,7 +122,7 @@ public class Process { if (peerUsed && peer!=null) { torpedoes.add(dock); } - Context.LoopFactory lf = ctx.new LoopFactory(dock, 1); + LoopFactory lf = new LoopFactory(ctx, dock, 1); lf.sendToken(ackDestination); reset_count++; } @@ -171,9 +171,9 @@ public class Process { } /** this port's peer (an OutPort) invokes this to have "recvToken" or equivalent inserted */ - public abstract void recvToken(Context.LoopFactory loopfactory_at_output_dock); + public abstract void recvToken(LoopFactory loopfactory_at_output_dock); /** this port's peer (an OutPort) invokes this to have "sendWord" or equivalent inserted */ - public abstract void sendWord(Context.LoopFactory loopfactory_at_output_dock); + public abstract void sendWord(LoopFactory loopfactory_at_output_dock); public int getTokensToAbsorb() { return 0; } } @@ -191,9 +191,9 @@ public class Process { } /** this port's peer (an InPort) invokes this to have "sendToken" or equivalent inserted */ - public abstract void sendToken(Context.LoopFactory loopfactory_at_input_dock); + public abstract void sendToken(LoopFactory loopfactory_at_input_dock); /** this port's peer (an InPort) invokes this to have "recvWord" or equivalent inserted */ - public abstract void recvWord(Context.LoopFactory loopfactory_at_input_dock); + public abstract void recvWord(LoopFactory loopfactory_at_input_dock); } public final class DockInPort extends InPort { @@ -208,9 +208,9 @@ public class Process { this.count = count; this.pattern = pattern; } - public void recvToken(Context.LoopFactory lf) { lf.recvToken(); } - public void sendWord(Context.LoopFactory lf) { lf.sendWord(dock.getDataDestination()); } - public void build(Context ctx) { build(ctx, ctx.new LoopFactory(dock, 1)); } + public void recvToken(LoopFactory lf) { lf.recvToken(); } + public void sendWord(LoopFactory lf) { lf.sendWord(dock.getDataDestination()); } + public void build(Context ctx) { build(ctx, new LoopFactory(ctx, dock, 1)); } // number-in-flight is considered a property of the input dock in a pair //public int getInflight() { return 4; } public int getInflight() { return 1; } @@ -223,7 +223,7 @@ public class Process { public void reset(Context ctx, int phase, Destination ackDestination) { doReset(ctx, phase, dock, peer, ackDestination, peerUsed()); } - protected void build(Context ctx, Context.LoopFactory lf) { + protected void build(Context ctx, LoopFactory lf) { int inflight = (count != 0 && count < getInflight()) ? count : getInflight(); if (peer!=null) @@ -260,10 +260,10 @@ public class Process { public final int count; public DockOutPort(String name, Dock dock) { this(name, dock, 0); } public DockOutPort(String name, Dock dock, int count) { super(name); this.dock = dock; this.count = count; } - public void sendToken(Context.LoopFactory lf) { lf.sendToken(dock.getDataDestination()); } - public void recvWord(Context.LoopFactory lf) { lf.recvWord(); } - public void build(Context ctx) { build(ctx, ctx.new LoopFactory(dock, 1)); } - protected void build(Context ctx, Context.LoopFactory lf) { + public void sendToken(LoopFactory lf) { lf.sendToken(dock.getDataDestination()); } + public void recvWord(LoopFactory lf) { lf.recvWord(); } + public void build(Context ctx) { build(ctx, new LoopFactory(ctx, dock, 1)); } + protected void build(Context ctx, LoopFactory lf) { if (peer==null) return; lf = lf.makeNext(count); lf.abortLoopIfTorpedoPresent(); @@ -293,8 +293,8 @@ public class Process { public class ForeverModule extends Module { private BitVector bv; public final OutPort out = new OutPort("out") { - public void sendToken(Context.LoopFactory lf) { } - public void recvWord(Context.LoopFactory lf) { } + public void sendToken(LoopFactory lf) { } + public void recvWord(LoopFactory lf) { } public void build(Context ctx) { } public void reset(Context ctx, int phase, Destination ackDestination) { } public void setPeer(InPort peer) { @@ -313,8 +313,8 @@ public class Process { public class OnceModule extends Module { private BitVector bv; public final OutPort out = new OutPort("out") { - public void sendToken(Context.LoopFactory lf) { } - public void recvWord(Context.LoopFactory lf) { } + public void sendToken(LoopFactory lf) { } + public void recvWord(LoopFactory lf) { } public void build(Context ctx) { } public void reset(Context ctx, int phase, Destination ackDestination) { } public void setPeer(InPort peer) { @@ -427,8 +427,8 @@ public class Process { this.inAddrWrite = new DockInPort("inAddrWrite", ship.getDock("inAddrWrite")); this.inDataWrite = new DockInPort("inDataWrite", ship.getDock("inDataWrite")); this.inAddrRead1 = new InPort("inAddrRead1") { - public void recvToken(Context.LoopFactory lf) { lf.recvToken(); } - public void sendWord(Context.LoopFactory lf) { lf.sendWord(ship.getDock("inAddrRead").getDataDestination(), new BitVector(1).set(0)); } + public void recvToken(LoopFactory lf) { lf.recvToken(); } + public void sendWord(LoopFactory lf) { lf.sendWord(ship.getDock("inAddrRead").getDataDestination(), new BitVector(1).set(0)); } public void build(Context ctx) { } public int getTokensToAbsorb() { return outRead1.peer.getTokensToAbsorb(); } public void reset(Context ctx, int phase, Destination ackDestination) { @@ -436,26 +436,26 @@ public class Process { } }; this.inAddrRead2 = new InPort("inAddrRead2") { - public void recvToken(Context.LoopFactory lf) { lf.recvToken(); } - public void sendWord(Context.LoopFactory lf) { lf.sendWord(ship.getDock("inAddrRead").getDataDestination(), new BitVector(1).set(1)); } + public void recvToken(LoopFactory lf) { lf.recvToken(); } + public void sendWord(LoopFactory lf) { lf.sendWord(ship.getDock("inAddrRead").getDataDestination(), new BitVector(1).set(1)); } public void build(Context ctx) { } public int getTokensToAbsorb() { return outRead2.peer.getTokensToAbsorb(); } public void reset(Context ctx, int phase, Destination ackDestination) { } }; this.outRead1 = new OutPort("outRead1") { - public void sendToken(Context.LoopFactory lf) { inAddrRead1.peer.sendToken(lf); } - public void recvWord(Context.LoopFactory lf) { lf.recvWord(); } + public void sendToken(LoopFactory lf) { inAddrRead1.peer.sendToken(lf); } + public void recvWord(LoopFactory lf) { lf.recvWord(); } public void build(Context ctx) { } public void reset(Context ctx, int phase, Destination ackDestination) { } }; this.outRead2 = new OutPort("outRead2") { - public void sendToken(Context.LoopFactory lf) { inAddrRead2.peer.sendToken(lf); } - public void recvWord(Context.LoopFactory lf) { lf.recvWord(); } + public void sendToken(LoopFactory lf) { inAddrRead2.peer.sendToken(lf); } + public void recvWord(LoopFactory lf) { lf.recvWord(); } public void build(Context ctx) { } public void reset(Context ctx, int phase, Destination ackDestination) { } }; this.outWrite = new DockOutPort("out", ship.getDock("out")) { - protected void build(Context ctx, Context.LoopFactory lf) { + protected void build(Context ctx, LoopFactory lf) { lf = lf.makeNext(0); lf.abortLoopIfTorpedoPresent(); lf.collectWord(); @@ -487,9 +487,9 @@ public class Process { } public void build(Context ctx) { super.build(ctx); - Context.LoopFactory lf; + LoopFactory lf; - lf = ctx.new LoopFactory(ship.getDock("inAddrRead"), 0); + lf = new LoopFactory(ctx, ship.getDock("inAddrRead"), 0); lf.abortLoopIfTorpedoPresent(); lf.recvWord(); lf.setFlags(FlagFunction.ZERO.add(FlagC), FlagFunction.ZERO); @@ -562,10 +562,10 @@ public class Process { Dock debugIn = debug.getDock("in"); Context ctx; - Context.LoopFactory lf; + LoopFactory lf; ctx = new Context(fp.getFleet()); - lf = ctx.new LoopFactory(debugIn, 1); + lf = new LoopFactory(ctx, debugIn, 1); lf.literal(12); lf.deliver(); lf.literal(5); @@ -588,7 +588,7 @@ public class Process { boolean reverse = (k%2)==0; - lf = ctx.new LoopFactory(debugIn, 4); + lf = new LoopFactory(ctx, debugIn, 4); lf.recvToken(); lf.setFlags(FlagFunction.ZERO.add(FlagC), FlagFunction.ZERO); lf.setPredicate(Predicate.NotFlagA); @@ -598,7 +598,7 @@ public class Process { lf.setPredicate(null); lf.deliver(); - lf = ctx.new LoopFactory(dock, 1); + lf = new LoopFactory(ctx, dock, 1); lf.sendToken(debugIn.getDataDestination(), new BitVector(1).set(reverse ? 1 : 0)); lf.sendToken(debugIn.getDataDestination(), new BitVector(1).set(reverse ? 0 : 1)); lf.sendToken(dock.getDataDestination(), new BitVector(1).set(reverse ? 1 : 0)); @@ -723,7 +723,7 @@ public class Process { fp.sendToken(debugIn.getInstructionDestination()); fp.flush(); - Context.LoopFactory lf = ctx2.new LoopFactory(debugIn, 0); + LoopFactory lf = new LoopFactory(ctx2, debugIn, 0); lf.literal(0); lf.abortLoopIfTorpedoPresent(); lf.recvToken(); @@ -744,21 +744,21 @@ public class Process { proc.reset(ctx2, phase, ackDestination); Context ctx3 = new Context(fp.getFleet()); - lf = ctx3.new LoopFactory(counter.getDock("inOp"), 1); + lf = new LoopFactory(ctx3, counter.getDock("inOp"), 1); lf.literal(9); lf.deliver(); lf.literal(5); lf.deliver(); - lf = ctx3.new LoopFactory(counter.getDock("in1"), 1); + lf = new LoopFactory(ctx3, counter.getDock("in1"), 1); lf.literal(reset_count-1); lf.deliver(); lf.literal(1); lf.deliver(); - lf = ctx3.new LoopFactory(counter.getDock("in2"), 0); + lf = new LoopFactory(ctx3, counter.getDock("in2"), 0); lf.abortLoopIfTorpedoPresent(); lf.recvWord(); lf.deliver(); - lf = ctx3.new LoopFactory(counter.getDock("out"), 1); + lf = new LoopFactory(ctx3, counter.getDock("out"), 1); lf.collectWord(); lf.sendToken(counter.getDock("in2").getInstructionDestination()); // HACK: we don't check to make sure this hits lf.sendToken(debugIn.getDataDestination()); diff --git a/src/edu/berkeley/fleet/loops/Context.java b/src/edu/berkeley/fleet/loops/Context.java index 5fd0851..7db5290 100644 --- a/src/edu/berkeley/fleet/loops/Context.java +++ b/src/edu/berkeley/fleet/loops/Context.java @@ -40,378 +40,22 @@ import static edu.berkeley.fleet.util.BitManipulations.*; * - after executing these remaining instructions, the dock's hatch is open */ public class Context { - private HashMap startupLoopFactories = new HashMap(); - private HashSet loopFactories = new HashSet(); public final Fleet fleet; - public Context(Fleet fleet) { this.fleet = fleet; } - private boolean autoflush = false; - public void setAutoflush(boolean a) { this.autoflush = a; } - - // FIXME: ability for a group of Contexts to share an "allocation - // pool" because they will run concurrently. - public HashSet allocatedShips = new HashSet(); + HashMap startupLoopFactories = new HashMap(); + HashSet loopFactories = new HashSet(); - public Ship allocateShip(String name) { - for(int i=0; ; i++) { - Ship ship = fleet.getShip(name, i); - if (ship==null) - throw new RuntimeException("no more ships of type " + name); - if (allocatedShips.contains(ship)) continue; - allocatedShips.add(ship); - return ship; - } + private final ShipPool pool; + public Context(Fleet fleet) { this(fleet, new ShipPool(fleet)); } + public Context(Fleet fleet, ShipPool pool) { + this.fleet = fleet; + this.pool = pool; } + public Ship allocateShip(String type) { return pool.allocateShip(type); } - // FIXME: is it legitimate to send a torpedo to a count==1 loop? - // FIXME: is it legitimate to send a torpedo to a count>1 loop? - /** - * - * A helper class for building loops of instructions. - * - * This class abstracts away: - * - The maximum size of a loop - * - The maximum length of a "one shot" instruction sequence - * - The looping/oneshot bit - * - The outer loop counter - * - The inner loop counter (opportunities to use it are auto-detected) - * - * It also performs various optimizations and provides a more - * convenient way of managing the predicate/interruptible fields. - * - * To get the most compact coding, the components of a Move should be - * performed in this order when possible, with no intervening commands: - * - * 1. recvToken() - * 2. recv()/collect() - * 3. sendToken() - * 4. deliver()/send() - * - */ - public class LoopFactory { - - // FIXME: vet this to see if it is sensible - private boolean instructionFifoSizeCheckDisabled = false; - public void disableInstructionFifoOverflowCheck() { instructionFifoSizeCheckDisabled = true; } - - public final Dock dock; - public final String friendlyName; - public final int count; - private Predicate predicate = Predicate.Default; - private LoopFactory next = null; - private ArrayList instructions = new ArrayList(); - - /** - * Creates a new loop. - * @arg dock the dock at which to execute the instructions - * @arg friendlyName a descriptive string for debugging the compiler - * @arg prev a loop for which this is the successor loop (if any) - * @arg count the number of times to execute this loop; 0 means continue until torpedoed - */ - public LoopFactory(Dock dock, int count) { this(dock, count, dock.toString(), null); } - public LoopFactory(Dock dock, int count, String friendlyName) { this(dock, count, friendlyName, null); } - private LoopFactory(Dock dock, int count, String friendlyName, LoopFactory prev) { - this.dock = dock; - this.count = count; - this.friendlyName = friendlyName; - Context.this.loopFactories.add(this); - if (Context.this.startupLoopFactories.get(dock) == null) - Context.this.startupLoopFactories.put(dock, this); - if (prev != null) { - if (prev.getNext() != null) throw new RuntimeException(); - prev.setNext(this); - } - } - - public LoopFactory makeNext(int new_count) { return makeNext(new_count, null); } - public LoopFactory makeNext(int new_count, String newFriendlyName) { - if (next != null) throw new RuntimeException("loop already has a successor"); - return new LoopFactory(dock, new_count, newFriendlyName, this); - } - public LoopFactory getNext() { return next; } - private void setNext(LoopFactory next) { - if (this.next != null) throw new RuntimeException("attempt to setNext() twice"); - this.next = next; - } - - - // Building Loops ////////////////////////////////////////////////////////////////////////////// - - boolean pending_interruptible = false; - boolean pending_recvToken = false; - boolean pending_recvOrCollect = false; - boolean pending_latchData = false; - boolean pending_latchPath = false; - boolean pending_sendToken = false; - Path pending_path = null; - - void flush_pending() { flush_pending(false); } - void flush_pending(boolean pending_dataOut) { - if (!pending_recvToken && - !pending_recvOrCollect && - !pending_sendToken && - !pending_dataOut) { - if (pending_interruptible) - throw new RuntimeException("abortLoopIfTorpedoPresent() must be followed immediately by a Move"); - } else { - instructions.add(new Move(dock, - count!=1, - predicate, - pending_interruptible, - pending_path==null ? null : pending_path, - pending_recvToken, - pending_recvOrCollect, - pending_latchData, - pending_latchPath, - pending_dataOut, - pending_sendToken)); - } - pending_interruptible = false; - pending_recvToken = false; - pending_recvOrCollect = false; - pending_latchData = false; - pending_latchPath = false; - pending_sendToken = false; - pending_path = null; - } - - public void interruptibleNop() { - flush_pending(); - instructions.add(new Move(dock, - count!=1, - predicate, - true, - null, - false, - false, - false, - false, - false, - false)); - } - - /** sets the predicate which will be applied to subsequent instructions, or null for the default predicate */ - public void setPredicate(Predicate p) { - if (p==null) p = Predicate.Default; - if (predicate==p) return; - flush_pending(); - predicate = p; - } - - /** must be followed immediately by a move-based instruction */ - public void abortLoopIfTorpedoPresent() { - flush_pending(); - //if (count!=0) throw new RuntimeException("currently, only forever-loops may be sensitive to torpedoes"); - pending_interruptible = true; - } - - /** [either] */ - public void recvToken() { - if (pending_recvToken || pending_recvOrCollect || pending_sendToken) flush_pending(); - pending_recvToken = true; - } - - /** [inboxes only] */ - public void recv(boolean latchData, boolean latchPath) { - if (!dock.isInputDock()) throw new RuntimeException("recv() may only be used at input docks"); - if (pending_recvOrCollect || pending_sendToken) flush_pending(); - pending_recvOrCollect = true; - pending_latchData = latchData; - pending_latchPath = latchPath; - } - - /** [outboxes only], will fuse with previous instruction if it was a recvToken() */ - public void collect(boolean latchData, boolean latchPath) { - if (!dock.isOutputDock()) throw new RuntimeException("collect() may only be used at output docks"); - if (pending_recvOrCollect || pending_sendToken) flush_pending(); - pending_recvOrCollect = true; - pending_latchData = latchData; - pending_latchPath = latchPath; - } - - /** [either], will fuse with previous instruction if it was a recvToken(), recv(), or collect() */ - public void sendToken(Destination dest) { sendToken(dest, null); } - public void sendToken(Destination dest, BitVector signal) { - if (pending_sendToken) flush_pending(); - pending_path = dock.getPath(dest, signal); - pending_sendToken = true; - } - - /** [inboxes only], will fuse with previous instruction if it was a sendToken() */ - public void deliver() { - if (!dock.isInputDock()) throw new RuntimeException("deliver() may only be used at input docks"); - flush_pending(true); - } - - /** [inboxes only], will fuse with previous instruction if it was a sendToken() */ - public void flush() { - if (!dock.isInputDock()) throw new RuntimeException("flush() may only be used at input docks"); - flush_pending(); - instructions.add(new Instruction.Flush(dock, count!=1, predicate)); - } - - /** [outboxes only], will fuse with previous instruction if it was a sendToken() */ - public void sendWord(Destination dest) { sendWord(dest, null); } - public void sendWord(Destination dest, BitVector signal) { - if (!dock.isOutputDock()) throw new RuntimeException("sendWord() may only be used at output docks"); - if (pending_sendToken) flush_pending(); - pending_path = dock.getPath(dest, signal); - flush_pending(true); - } - - /** sets the data latch to a literal value */ - public void literal(BitVector literal) { - // FIXME: code duplication here - // FIXME: be more intelligent here to avoid shifts if possible? - int counter = 0; - while(counter < dock.getShip().getFleet().getWordWidth()) counter += fleet.getShiftWidth(); - while(counter > 0) { - BitVector temp = new BitVector(dock.getShip().getFleet().getShiftWidth()); - for(int i=counter-1; i>=counter-fleet.getShiftWidth(); i--) - if (i 0) { - instructions.add(new Shift(dock, count!=1, predicate, - new BitVector(dock.getShip().getFleet().getWordWidth()) - .set(getField(counter-1, counter-fleet.getShiftWidth(), literal)))); - counter -= fleet.getShiftWidth(); - } - } - } - - /** sets the flags */ - public void setFlags(Instruction.Set.FlagFunction newFlagA, Instruction.Set.FlagFunction newFlagB) { - flush_pending(); - instructions.add(new Instruction.Set(dock, - count!=1, - predicate, - newFlagA, - newFlagB)); - } - - // FIXME: what if we're using an ILC-loop? - /** abort the loop immediately (if predicate is met) and invoke the successor loop */ - public void abort() { - flush_pending(); - instructions.add(new Instruction.Set(dock, - count!=1, - predicate, - SetDest.OuterLoopCounter, - 0)); - } - - public void abortAndInvoke(LoopFactory lf) { - throw new RuntimeException("not implemented"); - } - - // Emitting Code ////////////////////////////////////////////////////////////////////////////// - - void optimize() { - flush_pending(); - // FEATURE: find loops of 1 instruction, use ILC - // FEATURE: find sequences of >2 adjacent identical instructions, replace with use of ILC - // FEATURE: after optimizing, find single-instruction loops, replace with use of ILC - // FEATURE: consider doing loop unrolling if two copies of the loop fit in the instruction buffer... - // FEATURE: clever instruction re-oredering? - } - - /** - * The code emitted by this method makes the following assumptions: - * - * - The instructions emitted are dispatched in order - * - At the time of dispatch, the dock must be pre-quiescent. - * - */ - public void emit(ArrayList ic) { - flush_pending(); - optimize(); - - // FIXME: if this loop is a count==1 loop, we can emit the successor loop along with it... - - // the number of instructions after and including the first blocking instruction - int numInstructionsNotIncludingNonblockingPrefix = 0; - int loopSize = 0; - boolean blockingInstructionEncountered = false; - - // Set the OLC (it might previously have been zero) - ic.add(new Set(dock, false, Predicate.IgnoreFlagD, SetDest.OuterLoopCounter, count==0 ? 1 : count)); - if (count!=1) { - ic.add(new Instruction.Head(dock)); - } - - for(Instruction i : instructions) { - if (i instanceof Move && (((Move)i).tokenIn || ((Move)i).dataIn)) - blockingInstructionEncountered = true; - if (blockingInstructionEncountered) - numInstructionsNotIncludingNonblockingPrefix++; - loopSize++; - ic.add(i); - } - - if (count==1) { - if (!instructionFifoSizeCheckDisabled && - numInstructionsNotIncludingNonblockingPrefix > dock.getInstructionFifoSize()) - throw new RuntimeException("instruction sequence is too long for instruction fifo at " + dock); - } else { - if (count != 0) { - ic.add(new Instruction.Set(dock, true, Predicate.Default, SetDest.OuterLoopCounter, SetSource.Decrement)); - if (blockingInstructionEncountered) - numInstructionsNotIncludingNonblockingPrefix++; - loopSize++; - } - } - if (count!=1) { - ic.add(new Instruction.Abort(dock, Predicate.FlagD)); - } - if (autoflush && !"Debug".equals(dock.getShip().getType()) && next==null) { - if (dock.isInputDock()) - ic.add(new Instruction.Flush(dock, true, Predicate.FlagD)); - } - if (count!=1) { - ic.add(new Instruction.Tail(dock)); - if (!instructionFifoSizeCheckDisabled && - loopSize > dock.getInstructionFifoSize()) - throw new RuntimeException("instruction loop is too long for instruction fifo"); - } - - if (next != null) { - if (count != 1) throw new RuntimeException("no support for successor loops when count!=1 yet"); - // FIXME: must include check based on reduced FIFO capacity - // FIXME: review this - next.emit(ic); - } - } - - void warn(String warning) { - System.err.println("warning: " + warning); - } - - // Helpers ////////////////////////////////////////////////////////////////////////////// - - public void recvWord() { recv(true, false); } - public void recvPath() { recv(false, true); } - public void recvPacket() { recv(true, true); } - public void collectWord() { collect(true, false); } - public void collectPath() { collect(false, true); } - public void collectPacket() { collect(true, true); } - - } + boolean autoflush = false; + public void setAutoflush(boolean a) { this.autoflush = a; } public void emit(ArrayList ic) { for(LoopFactory lf : startupLoopFactories.values()) diff --git a/src/edu/berkeley/fleet/loops/LoopFactory.java b/src/edu/berkeley/fleet/loops/LoopFactory.java new file mode 100644 index 0000000..7d5649c --- /dev/null +++ b/src/edu/berkeley/fleet/loops/LoopFactory.java @@ -0,0 +1,365 @@ +package edu.berkeley.fleet.loops; +import java.util.*; +import java.net.*; +import edu.berkeley.fleet.two.*; +import edu.berkeley.fleet.api.*; +import edu.berkeley.fleet.api.Instruction.*; +import edu.berkeley.fleet.api.Instruction.Set; +import edu.berkeley.fleet.api.Instruction.Set.*; +import static edu.berkeley.fleet.util.BitManipulations.*; + +// FIXME: is it legitimate to send a torpedo to a count==1 loop? +// FIXME: is it legitimate to send a torpedo to a count>1 loop? +/** + * + * A helper class for building loops of instructions. + * + * This class abstracts away: + * - The maximum size of a loop + * - The maximum length of a "one shot" instruction sequence + * - The looping/oneshot bit + * - The outer loop counter + * - The inner loop counter (opportunities to use it are auto-detected) + * + * It also performs various optimizations and provides a more + * convenient way of managing the predicate/interruptible fields. + * + * To get the most compact coding, the components of a Move should be + * performed in this order when possible, with no intervening commands: + * + * 1. recvToken() + * 2. recv()/collect() + * 3. sendToken() + * 4. deliver()/send() + * + */ +public class LoopFactory { + + // FIXME: vet this to see if it is sensible + private boolean instructionFifoSizeCheckDisabled = false; + public void disableInstructionFifoOverflowCheck() { instructionFifoSizeCheckDisabled = true; } + + public final Dock dock; + public final String friendlyName; + public final int count; + + private final Context ctx; + private Predicate predicate = Predicate.Default; + private LoopFactory next = null; + private ArrayList instructions = new ArrayList(); + + /** + * Creates a new loop. + * @arg dock the dock at which to execute the instructions + * @arg friendlyName a descriptive string for debugging the compiler + * @arg prev a loop for which this is the successor loop (if any) + * @arg count the number of times to execute this loop; 0 means continue until torpedoed + */ + public LoopFactory(Context ctx, Dock dock, int count) { + this(ctx, dock, count, dock.toString(), null); + } + public LoopFactory(Context ctx, Dock dock, int count, String friendlyName) { + this(ctx, dock, count, friendlyName, null); + } + private LoopFactory(Context ctx, Dock dock, int count, String friendlyName, LoopFactory prev) { + this.ctx = ctx; + this.dock = dock; + this.count = count; + this.friendlyName = friendlyName; + ctx.loopFactories.add(this); + if (ctx.startupLoopFactories.get(dock) == null) + ctx.startupLoopFactories.put(dock, this); + if (prev != null) { + if (prev.getNext() != null) throw new RuntimeException(); + prev.setNext(this); + } + } + + public LoopFactory makeNext(int new_count) { return makeNext(new_count, null); } + public LoopFactory makeNext(int new_count, String newFriendlyName) { + if (next != null) throw new RuntimeException("loop already has a successor"); + return new LoopFactory(ctx, dock, new_count, newFriendlyName, this); + } + public LoopFactory getNext() { return next; } + private void setNext(LoopFactory next) { + if (this.next != null) throw new RuntimeException("attempt to setNext() twice"); + this.next = next; + } + + + // Building Loops ////////////////////////////////////////////////////////////////////////////// + + boolean pending_interruptible = false; + boolean pending_recvToken = false; + boolean pending_recvOrCollect = false; + boolean pending_latchData = false; + boolean pending_latchPath = false; + boolean pending_sendToken = false; + Path pending_path = null; + + void flush_pending() { flush_pending(false); } + void flush_pending(boolean pending_dataOut) { + if (!pending_recvToken && + !pending_recvOrCollect && + !pending_sendToken && + !pending_dataOut) { + if (pending_interruptible) + throw new RuntimeException("abortLoopIfTorpedoPresent() must be followed immediately by a Move"); + } else { + instructions.add(new Move(dock, + count!=1, + predicate, + pending_interruptible, + pending_path==null ? null : pending_path, + pending_recvToken, + pending_recvOrCollect, + pending_latchData, + pending_latchPath, + pending_dataOut, + pending_sendToken)); + } + pending_interruptible = false; + pending_recvToken = false; + pending_recvOrCollect = false; + pending_latchData = false; + pending_latchPath = false; + pending_sendToken = false; + pending_path = null; + } + + public void interruptibleNop() { + flush_pending(); + instructions.add(new Move(dock, + count!=1, + predicate, + true, + null, + false, + false, + false, + false, + false, + false)); + } + + /** sets the predicate which will be applied to subsequent instructions, or null for the default predicate */ + public void setPredicate(Predicate p) { + if (p==null) p = Predicate.Default; + if (predicate==p) return; + flush_pending(); + predicate = p; + } + + /** must be followed immediately by a move-based instruction */ + public void abortLoopIfTorpedoPresent() { + flush_pending(); + //if (count!=0) throw new RuntimeException("currently, only forever-loops may be sensitive to torpedoes"); + pending_interruptible = true; + } + + /** [either] */ + public void recvToken() { + if (pending_recvToken || pending_recvOrCollect || pending_sendToken) flush_pending(); + pending_recvToken = true; + } + + /** [inboxes only] */ + public void recv(boolean latchData, boolean latchPath) { + if (!dock.isInputDock()) throw new RuntimeException("recv() may only be used at input docks"); + if (pending_recvOrCollect || pending_sendToken) flush_pending(); + pending_recvOrCollect = true; + pending_latchData = latchData; + pending_latchPath = latchPath; + } + + /** [outboxes only], will fuse with previous instruction if it was a recvToken() */ + public void collect(boolean latchData, boolean latchPath) { + if (!dock.isOutputDock()) throw new RuntimeException("collect() may only be used at output docks"); + if (pending_recvOrCollect || pending_sendToken) flush_pending(); + pending_recvOrCollect = true; + pending_latchData = latchData; + pending_latchPath = latchPath; + } + + /** [either], will fuse with previous instruction if it was a recvToken(), recv(), or collect() */ + public void sendToken(Destination dest) { sendToken(dest, null); } + public void sendToken(Destination dest, BitVector signal) { + if (pending_sendToken) flush_pending(); + pending_path = dock.getPath(dest, signal); + pending_sendToken = true; + } + + /** [inboxes only], will fuse with previous instruction if it was a sendToken() */ + public void deliver() { + if (!dock.isInputDock()) throw new RuntimeException("deliver() may only be used at input docks"); + flush_pending(true); + } + + /** [inboxes only], will fuse with previous instruction if it was a sendToken() */ + public void flush() { + if (!dock.isInputDock()) throw new RuntimeException("flush() may only be used at input docks"); + flush_pending(); + instructions.add(new Instruction.Flush(dock, count!=1, predicate)); + } + + /** [outboxes only], will fuse with previous instruction if it was a sendToken() */ + public void sendWord(Destination dest) { sendWord(dest, null); } + public void sendWord(Destination dest, BitVector signal) { + if (!dock.isOutputDock()) throw new RuntimeException("sendWord() may only be used at output docks"); + if (pending_sendToken) flush_pending(); + pending_path = dock.getPath(dest, signal); + flush_pending(true); + } + + /** sets the data latch to a literal value */ + public void literal(BitVector literal) { + // FIXME: code duplication here + // FIXME: be more intelligent here to avoid shifts if possible? + int counter = 0; + while(counter < dock.getShip().getFleet().getWordWidth()) counter += ctx.fleet.getShiftWidth(); + while(counter > 0) { + BitVector temp = new BitVector(dock.getShip().getFleet().getShiftWidth()); + for(int i=counter-1; i>=counter-ctx.fleet.getShiftWidth(); i--) + if (i 0) { + instructions.add(new Shift(dock, count!=1, predicate, + new BitVector(dock.getShip().getFleet().getWordWidth()) + .set(getField(counter-1, counter-ctx.fleet.getShiftWidth(), literal)))); + counter -= ctx.fleet.getShiftWidth(); + } + } + } + + /** sets the flags */ + public void setFlags(Instruction.Set.FlagFunction newFlagA, Instruction.Set.FlagFunction newFlagB) { + flush_pending(); + instructions.add(new Instruction.Set(dock, + count!=1, + predicate, + newFlagA, + newFlagB)); + } + + // FIXME: what if we're using an ILC-loop? + /** abort the loop immediately (if predicate is met) and invoke the successor loop */ + public void abort() { + flush_pending(); + instructions.add(new Instruction.Set(dock, + count!=1, + predicate, + SetDest.OuterLoopCounter, + 0)); + } + + public void abortAndInvoke(LoopFactory lf) { + throw new RuntimeException("not implemented"); + } + + // Emitting Code ////////////////////////////////////////////////////////////////////////////// + + void optimize() { + flush_pending(); + // FEATURE: find loops of 1 instruction, use ILC + // FEATURE: find sequences of >2 adjacent identical instructions, replace with use of ILC + // FEATURE: after optimizing, find single-instruction loops, replace with use of ILC + // FEATURE: consider doing loop unrolling if two copies of the loop fit in the instruction buffer... + // FEATURE: clever instruction re-oredering? + } + + /** + * The code emitted by this method makes the following assumptions: + * + * - The instructions emitted are dispatched in order + * - At the time of dispatch, the dock must be pre-quiescent. + * + */ + public void emit(ArrayList ic) { + flush_pending(); + optimize(); + + // FIXME: if this loop is a count==1 loop, we can emit the successor loop along with it... + + // the number of instructions after and including the first blocking instruction + int numInstructionsNotIncludingNonblockingPrefix = 0; + int loopSize = 0; + boolean blockingInstructionEncountered = false; + + // Set the OLC (it might previously have been zero) + ic.add(new Set(dock, false, Predicate.IgnoreFlagD, SetDest.OuterLoopCounter, count==0 ? 1 : count)); + if (count!=1) { + ic.add(new Instruction.Head(dock)); + } + + for(Instruction i : instructions) { + if (i instanceof Move && (((Move)i).tokenIn || ((Move)i).dataIn)) + blockingInstructionEncountered = true; + if (blockingInstructionEncountered) + numInstructionsNotIncludingNonblockingPrefix++; + loopSize++; + ic.add(i); + } + + if (count==1) { + if (!instructionFifoSizeCheckDisabled && + numInstructionsNotIncludingNonblockingPrefix > dock.getInstructionFifoSize()) + throw new RuntimeException("instruction sequence is too long for instruction fifo at " + dock); + } else { + if (count != 0) { + ic.add(new Instruction.Set(dock, true, Predicate.Default, SetDest.OuterLoopCounter, SetSource.Decrement)); + if (blockingInstructionEncountered) + numInstructionsNotIncludingNonblockingPrefix++; + loopSize++; + } + } + if (count!=1) { + ic.add(new Instruction.Abort(dock, Predicate.FlagD)); + } + if (ctx.autoflush && !"Debug".equals(dock.getShip().getType()) && next==null) { + if (dock.isInputDock()) + ic.add(new Instruction.Flush(dock, true, Predicate.FlagD)); + } + if (count!=1) { + ic.add(new Instruction.Tail(dock)); + if (!instructionFifoSizeCheckDisabled && + loopSize > dock.getInstructionFifoSize()) + throw new RuntimeException("instruction loop is too long for instruction fifo"); + } + + if (next != null) { + if (count != 1) throw new RuntimeException("no support for successor loops when count!=1 yet"); + // FIXME: must include check based on reduced FIFO capacity + // FIXME: review this + next.emit(ic); + } + } + + void warn(String warning) { + System.err.println("warning: " + warning); + } + + // Helpers ////////////////////////////////////////////////////////////////////////////// + + public void recvWord() { recv(true, false); } + public void recvPath() { recv(false, true); } + public void recvPacket() { recv(true, true); } + public void collectWord() { collect(true, false); } + public void collectPath() { collect(false, true); } + public void collectPacket() { collect(true, true); } + +}