1 package edu.berkeley.fleet.loops;
4 import edu.berkeley.fleet.two.*;
5 import edu.berkeley.fleet.api.*;
6 import edu.berkeley.fleet.api.Instruction.*;
7 import edu.berkeley.fleet.api.Instruction.Set;
8 import edu.berkeley.fleet.api.Instruction.Set.*;
9 import static edu.berkeley.fleet.util.BitManipulations.*;
11 // FIXME: is it legitimate to send a torpedo to a count==1 loop?
12 // FIXME: is it legitimate to send a torpedo to a count>1 loop?
15 * A helper class for building loops of instructions.
17 * This class abstracts away:
18 * - The maximum size of a loop
19 * - The maximum length of a "one shot" instruction sequence
20 * - The looping/oneshot bit
21 * - The outer loop counter
22 * - The inner loop counter (opportunities to use it are auto-detected)
24 * It also performs various optimizations and provides a more
25 * convenient way of managing the predicate/interruptible fields.
27 * To get the most compact coding, the components of a Move should be
28 * performed in this order when possible, with no intervening commands:
36 public class LoopFactory {
38 // FIXME: vet this to see if it is sensible
39 private boolean instructionFifoSizeCheckDisabled = false;
40 public void disableInstructionFifoOverflowCheck() { instructionFifoSizeCheckDisabled = true; }
42 public final Dock dock;
43 public final String friendlyName;
44 public final int count;
46 private final Context ctx;
47 private Predicate predicate = Predicate.Default;
48 private LoopFactory next = null;
49 private ArrayList<Instruction> instructions = new ArrayList<Instruction>();
53 * @arg dock the dock at which to execute the instructions
54 * @arg friendlyName a descriptive string for debugging the compiler
55 * @arg prev a loop for which this is the successor loop (if any)
56 * @arg count the number of times to execute this loop; <tt>0</tt> means continue until torpedoed
58 public LoopFactory(Context ctx, Dock dock, int count) {
59 this(ctx, dock, count, dock.toString(), null);
61 public LoopFactory(Context ctx, Dock dock, int count, String friendlyName) {
62 this(ctx, dock, count, friendlyName, null);
64 private LoopFactory(Context ctx, Dock dock, int count, String friendlyName, LoopFactory prev) {
68 this.friendlyName = friendlyName;
69 ctx.loopFactories.add(this);
70 if (ctx.startupLoopFactories.get(dock) == null)
71 ctx.startupLoopFactories.put(dock, this);
73 if (prev.getNext() != null) throw new RuntimeException();
78 public LoopFactory makeNext(int new_count) { return makeNext(new_count, null); }
79 public LoopFactory makeNext(int new_count, String newFriendlyName) {
80 if (next != null) throw new RuntimeException("loop already has a successor");
81 return new LoopFactory(ctx, dock, new_count, newFriendlyName, this);
83 public LoopFactory getNext() { return next; }
84 private void setNext(LoopFactory next) {
85 if (this.next != null) throw new RuntimeException("attempt to setNext() twice");
90 // Building Loops //////////////////////////////////////////////////////////////////////////////
92 boolean pending_interruptible = false;
93 boolean pending_recvToken = false;
94 boolean pending_recvOrCollect = false;
95 boolean pending_latchData = false;
96 boolean pending_latchPath = false;
97 boolean pending_sendToken = false;
98 Path pending_path = null;
100 void flush_pending() { flush_pending(false); }
101 void flush_pending(boolean pending_dataOut) {
102 if (!pending_recvToken &&
103 !pending_recvOrCollect &&
104 !pending_sendToken &&
106 if (pending_interruptible)
107 throw new RuntimeException("abortLoopIfTorpedoPresent() must be followed immediately by a Move");
109 instructions.add(new Move(dock,
112 pending_interruptible,
113 pending_path==null ? null : pending_path,
115 pending_recvOrCollect,
121 pending_interruptible = false;
122 pending_recvToken = false;
123 pending_recvOrCollect = false;
124 pending_latchData = false;
125 pending_latchPath = false;
126 pending_sendToken = false;
130 public void interruptibleNop() {
132 instructions.add(new Move(dock,
145 /** sets the predicate which will be applied to subsequent instructions, or null for the default predicate */
146 public void setPredicate(Predicate p) {
147 if (p==null) p = Predicate.Default;
148 if (predicate==p) return;
153 /** must be followed immediately by a move-based instruction */
154 public void abortLoopIfTorpedoPresent() {
156 //if (count!=0) throw new RuntimeException("currently, only forever-loops may be sensitive to torpedoes");
157 pending_interruptible = true;
161 public void recvToken() {
162 if (pending_recvToken || pending_recvOrCollect || pending_sendToken) flush_pending();
163 pending_recvToken = true;
166 /** [inboxes only] */
167 public void recv(boolean latchData, boolean latchPath) {
168 if (!dock.isInputDock()) throw new RuntimeException("recv() may only be used at input docks");
169 if (pending_recvOrCollect || pending_sendToken) flush_pending();
170 pending_recvOrCollect = true;
171 pending_latchData = latchData;
172 pending_latchPath = latchPath;
175 /** [outboxes only], will fuse with previous instruction if it was a recvToken() */
176 public void collect(boolean latchData, boolean latchPath) {
177 if (!dock.isOutputDock()) throw new RuntimeException("collect() may only be used at output docks");
178 if (pending_recvOrCollect || pending_sendToken) flush_pending();
179 pending_recvOrCollect = true;
180 pending_latchData = latchData;
181 pending_latchPath = latchPath;
184 /** [either], will fuse with previous instruction if it was a recvToken(), recv(), or collect() */
185 public void sendToken(Destination dest) { sendToken(dest, null); }
186 public void sendToken(Destination dest, BitVector signal) {
187 if (pending_sendToken) flush_pending();
188 pending_path = dock.getPath(dest, signal);
189 pending_sendToken = true;
192 /** [inboxes only], will fuse with previous instruction if it was a sendToken() */
193 public void deliver() {
194 if (!dock.isInputDock()) throw new RuntimeException("deliver() may only be used at input docks");
198 /** [inboxes only], will fuse with previous instruction if it was a sendToken() */
199 public void flush() {
200 if (!dock.isInputDock()) throw new RuntimeException("flush() may only be used at input docks");
202 instructions.add(new Instruction.Flush(dock, count!=1, predicate));
205 /** [outboxes only], will fuse with previous instruction if it was a sendToken() */
206 public void sendWord(Destination dest) { sendWord(dest, null); }
207 public void sendWord(Destination dest, BitVector signal) {
208 if (!dock.isOutputDock()) throw new RuntimeException("sendWord() may only be used at output docks");
209 if (pending_sendToken) flush_pending();
210 pending_path = dock.getPath(dest, signal);
214 /** sets the data latch to a literal value */
215 public void literal(BitVector literal) {
216 // FIXME: code duplication here
217 // FIXME: be more intelligent here to avoid shifts if possible?
219 while(counter < dock.getShip().getFleet().getWordWidth()) counter += ctx.fleet.getShiftWidth();
221 BitVector temp = new BitVector(dock.getShip().getFleet().getShiftWidth());
222 for(int i=counter-1; i>=counter-ctx.fleet.getShiftWidth(); i--)
223 if (i<literal.length())
224 temp.set(i-(counter-ctx.fleet.getShiftWidth()), literal.get(i));
225 instructions.add(new Shift(dock, count!=1, predicate, temp));
226 counter -= ctx.fleet.getShiftWidth();
230 /** sets the data latch to a literal value */
231 public void literal(long literal) {
233 if (((FleetTwoFleet)ctx.fleet).isSmallEnoughToFit(literal)) {
234 instructions.add(new Instruction.Set(dock, count!=1, predicate, SetDest.DataLatch, literal));
238 while(counter < dock.getShip().getFleet().getWordWidth()) { extra++; counter += ctx.fleet.getShiftWidth(); }
239 warn("literal " + literal + " requires " + extra + " instructions");
241 instructions.add(new Shift(dock, count!=1, predicate,
242 new BitVector(dock.getShip().getFleet().getWordWidth())
243 .set(getField(counter-1, counter-ctx.fleet.getShiftWidth(), literal))));
244 counter -= ctx.fleet.getShiftWidth();
249 /** sets the flags */
250 public void setFlags(Instruction.Set.FlagFunction newFlagA, Instruction.Set.FlagFunction newFlagB) {
252 instructions.add(new Instruction.Set(dock,
259 // FIXME: what if we're using an ILC-loop?
260 /** abort the loop immediately (if predicate is met) and invoke the successor loop */
261 public void abort() {
263 instructions.add(new Instruction.Set(dock,
266 SetDest.OuterLoopCounter,
270 public void abortAndInvoke(LoopFactory lf) {
271 throw new RuntimeException("not implemented");
274 // Emitting Code //////////////////////////////////////////////////////////////////////////////
278 // FEATURE: find loops of 1 instruction, use ILC
279 // FEATURE: find sequences of >2 adjacent identical instructions, replace with use of ILC
280 // FEATURE: after optimizing, find single-instruction loops, replace with use of ILC
281 // FEATURE: consider doing loop unrolling if two copies of the loop fit in the instruction buffer...
282 // FEATURE: clever instruction re-oredering?
286 * The code emitted by this method makes the following assumptions:
288 * - The instructions emitted are dispatched in order
289 * - At the time of dispatch, the dock must be pre-quiescent.
292 public void emit(ArrayList<Instruction> ic) {
296 // FIXME: if this loop is a count==1 loop, we can emit the successor loop along with it...
298 // the number of instructions after and including the first blocking instruction
299 int numInstructionsNotIncludingNonblockingPrefix = 0;
301 boolean blockingInstructionEncountered = false;
303 // Set the OLC (it might previously have been zero)
304 ic.add(new Set(dock, false, Predicate.IgnoreFlagD, SetDest.OuterLoopCounter, count==0 ? 1 : count));
306 ic.add(new Instruction.Head(dock));
309 for(Instruction i : instructions) {
310 if (i instanceof Move && (((Move)i).tokenIn || ((Move)i).dataIn))
311 blockingInstructionEncountered = true;
312 if (blockingInstructionEncountered)
313 numInstructionsNotIncludingNonblockingPrefix++;
319 if (!instructionFifoSizeCheckDisabled &&
320 numInstructionsNotIncludingNonblockingPrefix > dock.getInstructionFifoSize())
321 throw new RuntimeException("instruction sequence is too long for instruction fifo at " + dock);
324 ic.add(new Instruction.Set(dock, true, Predicate.Default, SetDest.OuterLoopCounter, SetSource.Decrement));
325 if (blockingInstructionEncountered)
326 numInstructionsNotIncludingNonblockingPrefix++;
331 ic.add(new Instruction.Abort(dock, Predicate.FlagD));
333 if (ctx.autoflush && !"Debug".equals(dock.getShip().getType()) && next==null) {
334 if (dock.isInputDock())
335 ic.add(new Instruction.Flush(dock, true, Predicate.FlagD));
338 ic.add(new Instruction.Tail(dock));
339 if (!instructionFifoSizeCheckDisabled &&
340 loopSize > dock.getInstructionFifoSize())
341 throw new RuntimeException("instruction loop is too long for instruction fifo");
345 if (count != 1) throw new RuntimeException("no support for successor loops when count!=1 yet");
346 // FIXME: must include check based on reduced FIFO capacity
347 // FIXME: review this
352 void warn(String warning) {
353 System.err.println("warning: " + warning);
356 // Helpers //////////////////////////////////////////////////////////////////////////////
358 public void recvWord() { recv(true, false); }
359 public void recvPath() { recv(false, true); }
360 public void recvPacket() { recv(true, true); }
361 public void collectWord() { collect(true, false); }
362 public void collectPath() { collect(false, true); }
363 public void collectPacket() { collect(true, true); }