/src/kilim/analysis/CallWeaver.java

http://github.com/kilim/kilim · Java · 1083 lines · 652 code · 93 blank · 338 comment · 167 complexity · 3e711f9d80e22ed4c922c442b28dcd1b MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.analysis;
  7. import static kilim.Constants.ACC_ABSTRACT;
  8. import static kilim.Constants.D_FIBER_LAST_ARG;
  9. import static kilim.Constants.ALOAD_0;
  10. import static kilim.Constants.ASTORE_0;
  11. import static kilim.Constants.DLOAD_0;
  12. import static kilim.Constants.DSTORE_0;
  13. import static kilim.Constants.D_BOOLEAN;
  14. import static kilim.Constants.D_BYTE;
  15. import static kilim.Constants.D_CHAR;
  16. import static kilim.Constants.D_DOUBLE;
  17. import static kilim.Constants.D_FIBER;
  18. import static kilim.Constants.D_FLOAT;
  19. import static kilim.Constants.D_INT;
  20. import static kilim.Constants.D_LONG;
  21. import static kilim.Constants.D_NULL;
  22. import static kilim.Constants.D_OBJECT;
  23. import static kilim.Constants.D_SHORT;
  24. import static kilim.Constants.D_STATE;
  25. import static kilim.Constants.D_VOID;
  26. import static kilim.Constants.D_UNDEFINED;
  27. import static kilim.Constants.FIBER_CLASS;
  28. import static kilim.Constants.FLOAD_0;
  29. import static kilim.Constants.FSTORE_0;
  30. import static kilim.Constants.ILOAD_0;
  31. import static kilim.Constants.ISTORE_0;
  32. import static kilim.Constants.LLOAD_0;
  33. import static kilim.Constants.LSTORE_0;
  34. import static kilim.Constants.STATE_CLASS;
  35. import static kilim.analysis.VMType.TOBJECT;
  36. import static kilim.analysis.VMType.loadVar;
  37. import static kilim.analysis.VMType.storeVar;
  38. import static kilim.analysis.VMType.toVmType;
  39. import static org.objectweb.asm.Opcodes.ACONST_NULL;
  40. import static org.objectweb.asm.Opcodes.ALOAD;
  41. import static org.objectweb.asm.Opcodes.ARETURN;
  42. import static org.objectweb.asm.Opcodes.ASTORE;
  43. import static org.objectweb.asm.Opcodes.BIPUSH;
  44. import static org.objectweb.asm.Opcodes.CHECKCAST;
  45. import static org.objectweb.asm.Opcodes.DCONST_0;
  46. import static org.objectweb.asm.Opcodes.DCONST_1;
  47. import static org.objectweb.asm.Opcodes.DLOAD;
  48. import static org.objectweb.asm.Opcodes.DRETURN;
  49. import static org.objectweb.asm.Opcodes.DSTORE;
  50. import static org.objectweb.asm.Opcodes.DUP;
  51. import static org.objectweb.asm.Opcodes.FCONST_0;
  52. import static org.objectweb.asm.Opcodes.FCONST_1;
  53. import static org.objectweb.asm.Opcodes.FCONST_2;
  54. import static org.objectweb.asm.Opcodes.FLOAD;
  55. import static org.objectweb.asm.Opcodes.FRETURN;
  56. import static org.objectweb.asm.Opcodes.FSTORE;
  57. import static org.objectweb.asm.Opcodes.GETFIELD;
  58. import static org.objectweb.asm.Opcodes.GOTO;
  59. import static org.objectweb.asm.Opcodes.I2B;
  60. import static org.objectweb.asm.Opcodes.I2C;
  61. import static org.objectweb.asm.Opcodes.I2S;
  62. import static org.objectweb.asm.Opcodes.ICONST_0;
  63. import static org.objectweb.asm.Opcodes.ICONST_M1;
  64. import static org.objectweb.asm.Opcodes.ILOAD;
  65. import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
  66. import static org.objectweb.asm.Opcodes.INVOKESTATIC;
  67. import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
  68. import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
  69. import static org.objectweb.asm.Opcodes.IRETURN;
  70. import static org.objectweb.asm.Opcodes.ISTORE;
  71. import static org.objectweb.asm.Opcodes.LCONST_0;
  72. import static org.objectweb.asm.Opcodes.LCONST_1;
  73. import static org.objectweb.asm.Opcodes.LLOAD;
  74. import static org.objectweb.asm.Opcodes.LRETURN;
  75. import static org.objectweb.asm.Opcodes.LSTORE;
  76. import static org.objectweb.asm.Opcodes.NEW;
  77. import static org.objectweb.asm.Opcodes.POP;
  78. import static org.objectweb.asm.Opcodes.POP2;
  79. import static org.objectweb.asm.Opcodes.PUTFIELD;
  80. import static org.objectweb.asm.Opcodes.RETURN;
  81. import static org.objectweb.asm.Opcodes.SIPUSH;
  82. import java.util.ArrayList;
  83. import java.util.BitSet;
  84. import java.util.Collections;
  85. import kilim.mirrors.CachedClassMirrors.ClassMirror;
  86. import kilim.mirrors.CachedClassMirrors.MethodMirror;
  87. import kilim.mirrors.ClassMirrorNotFoundException;
  88. import kilim.mirrors.Detector;
  89. import org.objectweb.asm.tree.LabelNode;
  90. import org.objectweb.asm.tree.TableSwitchInsnNode;
  91. import org.objectweb.asm.MethodVisitor;
  92. import org.objectweb.asm.tree.MethodInsnNode;
  93. /**
  94. * This class produces all the code associated with a specific pausable method
  95. * invocation. There are three distinct chunks of code.
  96. * <dl>
  97. * <li> <b>Rewind</b>: At the beginning of the method, right after the opening
  98. * switch statement (switch fiber.pc), which is the "rewind" portion. This stage
  99. * pushes in (mostly) dummy values on the stack and jumps to the next method
  100. * invocation in the cycle. </li>
  101. *
  102. * <li> <b>Call</b>: The actual call. We push fiber as the last argument to the
  103. * pausable method (by calling fiber.down()), before making the call. </li>
  104. *
  105. * <li> <b>Post-call</b> The bulk of the code produced by this object. </li>
  106. * </ol>
  107. * <p>
  108. *
  109. * An explanation of some terms used in the code may be useful.
  110. *
  111. * Much of this code concerns itself with storing and retrieving values from/to
  112. * the stack and the local variables. The stack consists of three logical parts:<br>
  113. *
  114. * <pre>
  115. * +--------------+----------------------+---------------+
  116. * | Stack bottom | callee obj reference | args for call | (Top of stack)
  117. * +--------------+----------------------+---------------+
  118. * </pre>
  119. *
  120. * The callee's object reference and the arguments at the top of stack are
  121. * consumed by the method invocation. If the call is static, there is no object
  122. * reference, of course. The bottom of the stack (which may also be empty)
  123. * refers to the part of the stack that is left over once the args are consumed.
  124. * This is the part that we need to save if we have to yield.
  125. * <p>
  126. *
  127. * As for the local variables, we are interested in saving var 0 (the "this"
  128. * pointer) and all other variables that the flow analysis has shown to be
  129. * live-in, that is, is used downstream of the call. Typically, only about 30%
  130. * of all available vars are actually used downstream, so we use the rest for
  131. * temporary storage.
  132. * <p>
  133. *
  134. * @see #genRewind(MethodVisitor)
  135. * @see #genCall(MethodVisitor)
  136. * @see #genPostCall(MethodVisitor)
  137. *
  138. */
  139. public class CallWeaver {
  140. /**
  141. * The parent method-weaver responsible for writing the whole method
  142. */
  143. private MethodWeaver methodWeaver;
  144. /**
  145. * The basic block that calls the pausable method
  146. */
  147. BasicBlock bb;
  148. private LabelNode resumeLabel;
  149. LabelNode callLabel;
  150. private ValInfoList valInfoList;
  151. /**
  152. * varUsage[i] is true if the i-th var is taken. We don't touch the first
  153. * maxLocals vars. It is used for minimizing usage of extra vars.
  154. */
  155. BitSet varUsage;
  156. /**
  157. * number of local registers required.
  158. */
  159. int numVars;
  160. /** The canoncial name of the state class responsible for storing
  161. * the state (@see kilim.State)
  162. */
  163. private String stateClassName;
  164. /** Memoized version of getNumArgs() */
  165. int numArgs = -1;
  166. private Detector detector;
  167. public CallWeaver(MethodWeaver mw, Detector d, BasicBlock aBB) {
  168. detector = d;
  169. methodWeaver = mw;
  170. bb = aBB;
  171. callLabel = bb.startLabel;
  172. varUsage = new BitSet(2 * bb.flow.maxLocals);
  173. resumeLabel = bb.flow.getLabelAt(bb.startPos + 1);
  174. if (resumeLabel == null)
  175. resumeLabel = new LabelNode();
  176. assignRegisters();
  177. stateClassName = createStateClass();
  178. methodWeaver.ensureMaxStack(getNumBottom() + 3); //
  179. }
  180. /**
  181. * The basic block's frame tells us the number of parameters in the stack
  182. * and which local variables are needed later on.
  183. *
  184. * If the method is pausable, we'll need the bottom part of the stack and
  185. * the object reference from the top part of the stack to be able to resume
  186. * it. We don't need to worry about the arguments, because they will be
  187. * saved (if needed) in the _called_ method's state.
  188. *
  189. * The "this" arg (var0) is given special treatment. It is always saved in
  190. * all states, so it doesn't count as "data".
  191. */
  192. private void assignRegisters() {
  193. Frame f = bb.startFrame;
  194. MethodWeaver mw = methodWeaver;
  195. varUsage.set(mw.getFiberVar());
  196. numVars = mw.getFiberVar() + 1; // knowing fiberVar is beyond anything
  197. // that's used
  198. mw.ensureMaxVars(numVars);
  199. Usage u = bb.usage;
  200. valInfoList = new ValInfoList();
  201. /*
  202. * Create ValInfos for each Value that needs to be saved (all live-in
  203. * vars (except var 0, if not static) and all stack bottom vars count,
  204. * except if they are duplicates of earlier ones or are constants which
  205. * can be reproduced in bytecode itself.
  206. *
  207. * Process local vars before the stack, so that we can figure out which
  208. * elements of the stack are duplicates. Saving the stack requires more
  209. * processing, and the more we can reduce it, the better.
  210. */
  211. // ensure we don't touch any of the locals in the range that the
  212. // original
  213. // code knows about.
  214. varUsage.set(0, f.getMaxLocals());
  215. int i = bb.flow.isStatic() ? 0 : 1;
  216. for (; i < f.getMaxLocals(); i++) {
  217. Value v = f.getLocal(i);
  218. if (u.isLiveIn(i)) {
  219. if (!(v.isConstant() || valInfoList.contains(v))) {
  220. ValInfo vi = new ValInfo(v);
  221. vi.var = i;
  222. valInfoList.add(vi);
  223. }
  224. }
  225. }
  226. /*
  227. * All stack values at the bottom (those not consumed by the called
  228. * method) will have to be saved, if they are are not already accounted
  229. * for or they are constants.
  230. */
  231. int numBottom = getNumBottom();
  232. for (i = 0; i < numBottom; i++) {
  233. Value v = f.getStack(i);
  234. if (!(v.isConstant() || valInfoList.contains(v))) {
  235. ValInfo vi = new ValInfo(v);
  236. valInfoList.add(vi);
  237. }
  238. }
  239. Collections.sort(valInfoList); // sorts by type and var
  240. int fieldNum = 0;
  241. for (ValInfo vi : valInfoList) {
  242. vi.fieldName = "f" + fieldNum++;
  243. }
  244. }
  245. int getStackLen() {
  246. return bb.startFrame.getStackLen();
  247. }
  248. /**
  249. * The total number consumed by the call, including its object reference
  250. */
  251. int getNumArgs() {
  252. if (numArgs == -1) {
  253. numArgs = TypeDesc.getNumArgumentTypes(getMethodInsn().desc)
  254. + (isStaticCall() ? 0 : 1);
  255. }
  256. return numArgs;
  257. }
  258. final boolean isStaticCall() {
  259. return getMethodInsn().getOpcode() == INVOKESTATIC;
  260. }
  261. final MethodInsnNode getMethodInsn() {
  262. return (MethodInsnNode) bb.getInstruction(bb.startPos);
  263. }
  264. int getNumBottom() {
  265. return getStackLen() - getNumArgs();
  266. }
  267. /**
  268. * <pre>
  269. * The following template is produced in the method's prelude for each
  270. * pausable method.
  271. * F_REWIND:
  272. * for each bottom stack operand
  273. * introduce a dummy constant of the appropriate type.
  274. * (iconst_0, aconst_null, etc.)
  275. * if the call is not static,
  276. * we need the called object's object reference
  277. * ask the next state in the fiber's list
  278. * goto F_CALL: // jump to the invocation site.
  279. * </pre>
  280. *
  281. * @param mv
  282. */
  283. void genRewind(MethodVisitor mv) {
  284. Frame f = bb.startFrame;
  285. // The last parameter to the method is fiber, but the original
  286. // code doesn't know that and will use up that slot as it
  287. // pleases. If we are going to jump directly to the basicblock
  288. // bb, we'll have to make sure it has some dummy value of that
  289. // type going in just to keep the verifier happy.
  290. for (int i = methodWeaver.getFiberArgVar(); i < f.getMaxLocals(); ) {
  291. Value v = f.getLocal(i);
  292. if (v.getTypeDesc() != D_UNDEFINED) {
  293. // if (u.isLiveIn(i)) {
  294. int vmt = toVmType(v.getTypeDesc());
  295. mv.visitInsn(VMType.constInsn[vmt]);
  296. storeVar(mv, vmt, i);
  297. }
  298. i += v.isCategory2() ? 2 : 1;
  299. }
  300. // store dummy values in stack. The constants have to correspond
  301. // to the types of each of the bottom elements.
  302. int numBottom = getNumBottom();
  303. // spos == stack pos. Note that it is incremented beyond the
  304. // 'for' loop below.
  305. int spos;
  306. for (spos = 0; spos < numBottom; spos++) {
  307. Value v = f.getStack(spos);
  308. if (v.isConstant()) {
  309. mv.visitInsn(VMType.constInsn[VMType.toVmType(v.getTypeDesc())]);
  310. } else {
  311. ValInfo vi = valInfoList.find(v);
  312. mv.visitInsn(VMType.constInsn[vi.vmt]);
  313. }
  314. }
  315. if (!isStaticCall()) {
  316. // The next element in the stack after numBottom is the object
  317. // reference for the callee. This can't be a dummy because it
  318. // is needed by the invokevirtual call. If this reference
  319. // is found in the local vars, we'll use it, otherwise we need
  320. // to dip into the next activation frame in the fiber to
  321. // retrieve it.
  322. Value v = f.getStack(numBottom);
  323. if (!methodWeaver.isStatic() && f.getLocal(0) == v) {
  324. // "this" is already properly initialized.
  325. mv.visitVarInsn(ALOAD, 0);
  326. } else {
  327. loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
  328. mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "getCallee", "()Ljava/lang/Object;", false);
  329. mv.visitTypeInsn(CHECKCAST, getReceiverTypename());
  330. }
  331. spos++;
  332. }
  333. int len = f.getStackLen();
  334. // fill rest of stack with dummy args
  335. for (; spos < len; spos++) {
  336. Value v = f.getStack(spos);
  337. int vmt = VMType.toVmType(v.getTypeDesc());
  338. mv.visitInsn(VMType.constInsn[vmt]);
  339. }
  340. mv.visitJumpInsn(GOTO, callLabel.getLabel());
  341. }
  342. /**
  343. * Before we make the target call, we need to call fiber.down(), to update
  344. * it on the depth of the stack. genPostCall subsequently arranges to call
  345. * fiber.up(). We also need to push the fiber as the last argument to the
  346. * pausable method. We accomplish both of these objectives by having
  347. * fiber.down() do its book-keeping and return fiber, which is left on the
  348. * stack before the call is made.
  349. *
  350. * <pre>
  351. *
  352. * F_CALL:
  353. * push fiber.down()
  354. * invoke[virtual|static] classname/method modifiedDesc
  355. * </pre>
  356. *
  357. * @param mv
  358. */
  359. void genCall(MethodVisitor mv) {
  360. mv.visitLabel(callLabel.getLabel());
  361. loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
  362. mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "down", "()" + D_FIBER, false);
  363. MethodInsnNode mi = getMethodInsn();
  364. if (isSAM(mi)) {
  365. ClassWeaver cw = methodWeaver.getClassWeaver();
  366. SAMweaver sw = cw.getSAMWeaver(mi.owner, mi.name, mi.desc, mi.itf);
  367. //System.out.println("SAM call to: " + mi.owner + mi.name + mi.desc);
  368. //System.out.println("invokestatic: " + cw.getName() + " " + sw.getShimMethodName());
  369. mi = new MethodInsnNode(INVOKESTATIC, cw.getName(), sw.getShimMethodName(),
  370. sw.getShimDesc(), cw.isInterface());
  371. }
  372. if (mi.desc.indexOf(D_FIBER_LAST_ARG) == -1) {
  373. // Don't add another fiberarg if it already has one. It'll already
  374. // have one if we have copied jsr instructions and modified the
  375. // same instruction node earlier.
  376. mi.desc = mi.desc.replace(")", D_FIBER_LAST_ARG);
  377. }
  378. mi.accept(mv);
  379. }
  380. /**
  381. * Is the given method the sole abstract method (modulo woven variants).
  382. */
  383. boolean isSAM(MethodInsnNode mi) {
  384. Detector det = this.detector;
  385. int count = 0;
  386. boolean match = false;
  387. try {
  388. ClassMirror cm = det.classForName(mi.owner);
  389. // java7 can't call java8, but java8 can call java7 - only check the callee
  390. if (cm.version() < 52)
  391. return false;
  392. for (MethodMirror m: cm.getDeclaredMethods()) {
  393. if (m.getMethodDescriptor().indexOf(D_FIBER_LAST_ARG) == -1) {
  394. if ((m.getModifiers() & ACC_ABSTRACT) > 0) {
  395. count++;
  396. if (m.getName().equals(mi.name) && m.getMethodDescriptor().equals(mi.desc)) {
  397. match = true;
  398. }
  399. }
  400. }
  401. }
  402. } catch (ClassMirrorNotFoundException ignore) {
  403. /* This error would have been caught earlier in MethodFlow, if the class can't be found */
  404. }
  405. return match && (count == 1);
  406. }
  407. /**
  408. * After the pausable method call is over, we have four possibilities. The
  409. * called method yielded, or it returned normally. Orthogonally, we have
  410. * saved state or not. fiber.up() returns the combined status
  411. *
  412. ? * <pre>
  413. *
  414. * switch (fiber.up()) {
  415. * default:
  416. * 0: goto RESUME; // Not yielding , no State -- resume normal operations
  417. * 1: goto RESTORE; // Not yielding, has State -- restore state before resuming
  418. * 2: goto SAVE; // Yielding, no state -- save state before unwinding stack
  419. * 3: goto UNWIND // Yielding, has state -- nothing to do but unwind.
  420. * }
  421. * SAVE:
  422. * ...
  423. * xRETURN
  424. * UNWIND:
  425. * ...
  426. * xRETURN
  427. * RESTORE:
  428. * ...
  429. * // fall through to RESUME
  430. * RESUME:
  431. * ... original code
  432. * </pre>
  433. *
  434. * @param mv
  435. *
  436. */
  437. void genPostCall(MethodVisitor mv) {
  438. loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
  439. mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "up", "()I", false);
  440. LabelNode restoreLabel = new LabelNode();
  441. LabelNode saveLabel = new LabelNode();
  442. LabelNode unwindLabel = new LabelNode();
  443. LabelNode[] labels = new LabelNode[] { resumeLabel, restoreLabel, saveLabel,
  444. unwindLabel };
  445. new TableSwitchInsnNode(0, 3, resumeLabel, labels).accept(mv);
  446. genSave(mv, saveLabel);
  447. genUnwind(mv, unwindLabel);
  448. genRestore(mv, restoreLabel);
  449. resumeLabel.accept(mv);
  450. }
  451. /**
  452. * Code for the case where we are yielding, and we have state built up from
  453. * a previous call. There's nothing meaningful to do except keep the
  454. * verifier happy. Pop the bottom stack, then return a dummy return value
  455. * (if this method -- note: not the called method -- returns a value)
  456. *
  457. * @param mv
  458. */
  459. private void genUnwind(MethodVisitor mv, LabelNode unwindLabel) {
  460. unwindLabel.accept(mv);
  461. // After the call returns, the stack would be left with numBottom plus
  462. // return value
  463. // pop callee's dummy return value first
  464. String rdesc = getReturnType();
  465. if (rdesc != D_VOID) {
  466. mv.visitInsn(TypeDesc.isDoubleWord(rdesc) ? POP2 : POP);
  467. }
  468. // pop numbottom
  469. int i = getNumBottom() - 1; // the numBottom'th element
  470. Frame f = bb.startFrame;
  471. for (; i >= 0; i--) {
  472. mv.visitInsn(f.getStack(i).isCategory1() ? POP : POP2);
  473. }
  474. // Now for the return value of this (the calling) method
  475. rdesc = TypeDesc.getReturnTypeDesc(bb.flow.desc);
  476. if (rdesc != D_VOID) {
  477. // ICONST_0; IRETURN or ACONST_NULL; ARETURN etc.
  478. int vmt = VMType.toVmType(rdesc);
  479. mv.visitInsn(VMType.constInsn[vmt]);
  480. mv.visitInsn(VMType.retInsn[vmt]);
  481. } else {
  482. mv.visitInsn(RETURN);
  483. }
  484. }
  485. private String getReturnType() {
  486. return TypeDesc.getReturnTypeDesc(getMethodInsn().desc);
  487. }
  488. /**
  489. * Yielding, but state hasn't been captured yet. We create a state object
  490. * and save each object in valInfoList in its corresponding field.
  491. *
  492. * Note that we save each stack item into a scratch register before loading
  493. * it into a field. The reason is that we need to get the State ref under
  494. * the stack item before we can do a putfield. The alternative is to load
  495. * the State item, then do a swap or a dup_x2;pop (depending on the value's
  496. * category). We'll go with the earlier approach because stack manipulations
  497. * don't seem to perform as well in the current crop of JVMs.
  498. *
  499. * @param mv
  500. */
  501. private void genSave(MethodVisitor mv, LabelNode saveLabel) {
  502. saveLabel.accept(mv);
  503. Frame f = bb.startFrame;
  504. // pop return value if any.
  505. String retType = getReturnType();
  506. if (retType != D_VOID) {
  507. // Yielding, so the return value from the called
  508. // function is a dummy value
  509. mv.visitInsn(TypeDesc.isDoubleWord(retType) ? POP2 : POP);
  510. }
  511. /*
  512. * Instantiate state class. Call one of new xxxState(this, pc, fiber),
  513. * or new xxxState(pc, fiber) depending whether this method is static or
  514. * not. Note that are not referring to the called method.
  515. *
  516. * pc, "the program counter" is merely the index of the call weaver in
  517. * the method weaver's list. This allows us to do a switch in the
  518. * method's entry.
  519. */
  520. mv.visitTypeInsn(NEW, stateClassName);
  521. mv.visitInsn(DUP); //
  522. // call constructor
  523. mv.visitMethodInsn(INVOKESPECIAL, stateClassName, "<init>", "()V", false);
  524. // save state in register
  525. int stateVar = allocVar(1);
  526. storeVar(mv, TOBJECT, stateVar);
  527. // state.self = this if the current executing method isn't static
  528. if (!bb.flow.isStatic()) {
  529. loadVar(mv, TOBJECT, stateVar);
  530. mv.visitVarInsn(ALOAD, 0); // for state.self == this
  531. mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "self", D_OBJECT);
  532. }
  533. int pc = methodWeaver.getPC(this);
  534. loadVar(mv, TOBJECT, stateVar); // state.pc
  535. if (pc < 6) {
  536. mv.visitInsn(ICONST_0 + pc);
  537. } else {
  538. mv.visitIntInsn(BIPUSH, pc);
  539. }
  540. mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "pc", D_INT);
  541. // First save bottom stack into state
  542. int i = getNumBottom() - 1;
  543. for (; i >= 0; i--) {
  544. Value v = f.getStack(i);
  545. ValInfo vi = valInfoList.find(v);
  546. if (vi == null) {
  547. // it must be a constant or a duplicate, which is why we don't
  548. // have any information on it. just pop it.
  549. mv.visitInsn(v.category() == 2 ? POP2 : POP);
  550. } else {
  551. /**
  552. * xstore <scratchVar> ; send stack elem to some local var
  553. * aload <stateVar> ; load state
  554. * xload <scratchVar> ; get stack elem back
  555. * putfield ... ; state.fx =
  556. */
  557. int var = allocVar(vi.val.category());
  558. storeVar(mv, vi.vmt, var);
  559. loadVar(mv, VMType.TOBJECT, stateVar);
  560. loadVar(mv, vi.vmt, var);
  561. mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
  562. releaseVar(var, vi.val.category());
  563. }
  564. }
  565. // Now load up registers into fields
  566. for (ValInfo vi : valInfoList) {
  567. // Ignore values on stack
  568. if (vi.var == -1)
  569. continue;
  570. // aload state var
  571. // xload <var>
  572. loadVar(mv, TOBJECT, stateVar);
  573. loadVar(mv, vi.vmt, vi.var);
  574. mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
  575. }
  576. // Fiber.setState(state);
  577. loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
  578. loadVar(mv, TOBJECT, stateVar);
  579. mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "setState", "(" + D_STATE + ")V", false);
  580. releaseVar(stateVar, 1);
  581. // Figure out the return type of the calling method and issue the
  582. // appropriate xRETURN instruction
  583. retType = TypeDesc.getReturnTypeDesc(bb.flow.desc);
  584. if (retType == D_VOID) {
  585. mv.visitInsn(RETURN);
  586. } else {
  587. int vmt = VMType.toVmType(retType);
  588. // ICONST_0, DCONST_0 etc.
  589. mv.visitInsn(VMType.constInsn[vmt]);
  590. // IRETURN, DRETURN, etc.
  591. mv.visitInsn(VMType.retInsn[vmt]);
  592. }
  593. }
  594. /**
  595. * Not yielding (resuming normally), but have stored state. We need to
  596. * restore from state before resuming. This is slightly more work than
  597. * saving state, because we have to restore constants and duplicates too.
  598. * <b>
  599. *
  600. * Note that the return value (if any) has a real value, which is why it
  601. * needs to be saved away before we can get access to the bottom elements to
  602. * pop them out.
  603. *
  604. * <pre>
  605. * If there is anything at the bottom save return value in scratch register
  606. * pop unnecessary bottom stuff.
  607. *
  608. * load fiber.curState
  609. * cast to specific state (if necessary)
  610. * astore in scratch &lt;stateVar&gt;
  611. *
  612. * for each value in frame.var
  613. * if val is constant or is in valInfoList,
  614. * push constant or load field (appropriately)
  615. * for each value in bottom stack
  616. * restore value similar to above
  617. * restore return value if any from scratch register
  618. * </pre>
  619. */
  620. private void genRestore(MethodVisitor mv, LabelNode restoreLabel) {
  621. restoreLabel.accept(mv);
  622. Frame f = bb.startFrame;
  623. int numBottom = getNumBottom();
  624. int retVar = -1;
  625. int retctype = -1;
  626. if (numBottom > 0) {
  627. // We have dummy values sitting in the stack. pop 'em.
  628. // But first, check if we have a real return value on top
  629. String retType = getReturnType();
  630. if (retType != D_VOID) {
  631. // .. yep. save it to scratch register
  632. retctype = VMType.toVmType(retType);
  633. retVar = allocVar(VMType.category[retctype]);
  634. storeVar(mv, retctype, retVar);
  635. }
  636. // pop dummy values from stack bottom
  637. for (int i = numBottom-1; i >= 0; i--) {
  638. Value v = f.getStack(i);
  639. int insn = v.isCategory1() ? POP : POP2;
  640. mv.visitInsn(insn);
  641. }
  642. }
  643. // Restore variables from state
  644. int stateVar = -1;
  645. if (valInfoList.size() > 0) {
  646. stateVar = allocVar(1);
  647. }
  648. genRestoreVars(mv, stateVar);
  649. // Now restore the bottom values in the stack from state
  650. for (int i = 0; i < numBottom; i++) {
  651. Value v = f.getStack(i);
  652. if (v.isConstant()) {
  653. loadConstant(mv, v);
  654. } else {
  655. ValInfo vi = valInfoList.find(v);
  656. if (vi.var == -1) {
  657. loadVar(mv, TOBJECT, stateVar);
  658. mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
  659. checkcast(mv, v);
  660. } else {
  661. // this stack value is a duplicate of a local var, which has
  662. // already been loaded and is of the right type
  663. loadVar(mv, vi.vmt, vi.var);
  664. }
  665. }
  666. }
  667. // restore the saved return value, if any
  668. // But we would have popped and saved the return value only
  669. // if there were any dummy values in the stack bottom to be
  670. // cleared out. If not, we wouldn't have bothered
  671. // popping the return value in the first place.
  672. if (numBottom > 0) {
  673. if (retVar != -1) {
  674. loadVar(mv, retctype, retVar);
  675. }
  676. }
  677. releaseVar(stateVar, 1);
  678. if (retctype != -1) {
  679. releaseVar(retVar, VMType.category[retctype]);
  680. }
  681. // Fall through to the resumeLabel in genNY_NS, so no goto required.
  682. }
  683. void genRestoreEx(MethodVisitor mv, LabelNode restoreLabel) {
  684. restoreLabel.accept(mv);
  685. int stateVar = -1;
  686. if (valInfoList.size() > 0) {
  687. stateVar = allocVar(1);
  688. }
  689. genRestoreVars(mv, stateVar);
  690. releaseVar(stateVar, 1);
  691. }
  692. /*
  693. */
  694. private void genRestoreVars(MethodVisitor mv, int stateVar) {
  695. Frame f = bb.startFrame;
  696. if (valInfoList.size() > 0) {
  697. // need to have state in a local variable
  698. loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
  699. mv.visitFieldInsn(GETFIELD, FIBER_CLASS, "curState", D_STATE);
  700. if (!stateClassName.equals(STATE_CLASS)) {
  701. mv.visitTypeInsn(CHECKCAST, stateClassName);
  702. }
  703. storeVar(mv, TOBJECT, stateVar);
  704. }
  705. // no need to restore "this" if it's already there.
  706. Usage u = bb.usage;
  707. int len = f.getMaxLocals();
  708. int i = bb.flow.isStatic() ? 0 : 1;
  709. for (; i < len; i++) {
  710. if (!u.isLiveIn(i))
  711. continue;
  712. Value v = f.getLocal(i);
  713. int vmt = VMType.toVmType(v.getTypeDesc());
  714. if (v.isConstant()) {
  715. loadConstant(mv, v);
  716. } else {
  717. ValInfo vi = valInfoList.find(v);
  718. if (vi.var == i) {
  719. // load val from state
  720. loadVar(mv, TOBJECT, stateVar);
  721. mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
  722. checkcast(mv, v); // don't need to do this in the constant case
  723. } else {
  724. // It is a duplicate of another var. No need to load this var from stack
  725. assert vi.var < i;
  726. loadVar(mv, vi.vmt, vi.var);
  727. }
  728. }
  729. // Convert types from vm types to value's real type, if necessary
  730. // store into local
  731. storeVar(mv, vmt, i);
  732. }
  733. releaseVar(stateVar, 1);
  734. }
  735. private String getReceiverTypename() {
  736. MethodInsnNode min = getMethodInsn();
  737. return min.owner;
  738. }
  739. /**
  740. * We have loaded a value of one of the five VM types into the stack and we
  741. * need to cast it to the value's type, if necessary
  742. *
  743. * @param mv
  744. * @param v
  745. */
  746. private void checkcast(MethodVisitor mv, Value v) {
  747. String valType = v.getTypeDesc();
  748. int vmt = VMType.toVmType(valType);
  749. switch (vmt) {
  750. case VMType.TOBJECT:
  751. if (valType == D_OBJECT || valType == D_NULL) {
  752. return;
  753. }
  754. mv.visitTypeInsn(CHECKCAST, TypeDesc.getInternalName(valType));
  755. break;
  756. case VMType.TINT:
  757. if (valType == D_INT)
  758. return;
  759. int insn = 0;
  760. if (valType == D_SHORT)
  761. insn = I2S;
  762. else if (valType == D_BYTE)
  763. insn = I2B;
  764. else if (valType == D_CHAR)
  765. insn = I2C;
  766. else
  767. assert valType == D_BOOLEAN;
  768. mv.visitInsn(insn);
  769. break;
  770. default:
  771. break;
  772. }
  773. }
  774. private void loadConstant(MethodVisitor mv, Value v) {
  775. if (v.getTypeDesc() == D_NULL) {
  776. mv.visitInsn(ACONST_NULL);
  777. return;
  778. }
  779. Object c = v.getConstVal();
  780. if (c instanceof Integer) {
  781. int i = (Integer) c;
  782. if (i > -1 && i <= 5) {
  783. mv.visitInsn(i + 1 + ICONST_M1);
  784. return;
  785. } else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
  786. mv.visitIntInsn(BIPUSH, i);
  787. return;
  788. } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
  789. mv.visitIntInsn(SIPUSH, i);
  790. return;
  791. }
  792. } else if (c instanceof Float) {
  793. Float f = ((Float) c).floatValue();
  794. int insn = 0;
  795. if (f == 0.0)
  796. insn = FCONST_0;
  797. else if (f == 1.0)
  798. insn = FCONST_1;
  799. else if (f == 2.0)
  800. insn = FCONST_2;
  801. if (insn != 0) {
  802. mv.visitInsn(insn);
  803. return;
  804. }
  805. } else if (c instanceof Long) {
  806. Long l = ((Long) c).longValue();
  807. int insn = 0;
  808. if (l == 0L)
  809. insn = LCONST_0;
  810. else if (l == 1L)
  811. insn = LCONST_1;
  812. if (insn != 0) {
  813. mv.visitInsn(insn);
  814. return;
  815. }
  816. } else if (c instanceof Double) {
  817. Double d = ((Double) c).doubleValue();
  818. int insn = 0;
  819. if (d == 0.0)
  820. insn = DCONST_0;
  821. else if (d == 1.0)
  822. insn = DCONST_1;
  823. if (insn != 0) {
  824. mv.visitInsn(insn);
  825. return;
  826. }
  827. }
  828. // No special constants found.
  829. mv.visitLdcInsn(c);
  830. }
  831. private String createStateClass() {
  832. return valInfoList.size() == 0 ? STATE_CLASS :
  833. methodWeaver.createStateClass(valInfoList);
  834. }
  835. private int allocVar(int size) {
  836. int var;
  837. for (var = 0;; var++) {
  838. // if the var'th local is not set (if double, check the next word
  839. // too)
  840. if (!varUsage.get(var)) {
  841. if (size == 1 || !varUsage.get(var + 1)) {
  842. break;
  843. }
  844. }
  845. }
  846. varUsage.set(var);
  847. if (size == 2) {
  848. varUsage.set(var + 1);
  849. methodWeaver.ensureMaxVars(var + 2); // var is 0-based
  850. } else {
  851. methodWeaver.ensureMaxVars(var + 1);
  852. }
  853. return var;
  854. }
  855. private void releaseVar(int var, int size) {
  856. if (var == -1)
  857. return;
  858. varUsage.clear(var);
  859. if (size == 2) {
  860. varUsage.clear(var + 1);
  861. }
  862. }
  863. BasicBlock getBasicBlock() {
  864. return bb;
  865. }
  866. }
  867. class ValInfo implements Comparable<ValInfo> {
  868. /**
  869. * The var to which the value belongs. It remains undefined if it is a stack
  870. * item.
  871. */
  872. int var = -1;
  873. /**
  874. * The value to hold. This gives us information about the type, whether the
  875. * value is duplicated and whether it is a constant value.
  876. */
  877. Value val;
  878. /**
  879. * The type of value boiled down to one of the canonical types.
  880. */
  881. int vmt;
  882. /**
  883. * Names of the fields in the state var: "f0", "f1", etc, according to their
  884. * position in the call weaver's valInfoList.
  885. */
  886. String fieldName;
  887. ValInfo(Value v) {
  888. val = v;
  889. vmt = VMType.toVmType(v.getTypeDesc());
  890. }
  891. String fieldDesc() {
  892. return VMType.fieldDesc[vmt];
  893. }
  894. public int compareTo(ValInfo that) {
  895. if (this == that)
  896. return 0;
  897. if (this.vmt < that.vmt)
  898. return -1;
  899. if (this.vmt > that.vmt)
  900. return 1;
  901. if (this.var < that.var)
  902. return -1;
  903. if (this.var > that.var)
  904. return 1;
  905. return 0;
  906. }
  907. }
  908. class ValInfoList extends ArrayList<ValInfo> {
  909. private static final long serialVersionUID = -2339264992519046024L;
  910. public ValInfo find(Value v) {
  911. int i = indexOf(v);
  912. return (i == -1) ? null : get(i);
  913. }
  914. public int indexOf(Value v) {
  915. int len = size();
  916. for (int i = 0; i < len; i++) {
  917. if (get(i).val == v)
  918. return i;
  919. }
  920. return -1;
  921. }
  922. public boolean contains(Value v) {
  923. return indexOf(v) != -1;
  924. }
  925. }
  926. class VMType {
  927. static final int TOBJECT = 0;
  928. static final int TINT = 1;
  929. static final int TLONG = 2;
  930. static final int TDOUBLE = 3;
  931. static final int TFLOAT = 4;
  932. static final int[] constInsn = { ACONST_NULL, ICONST_0, LCONST_0,
  933. DCONST_0, FCONST_0 };
  934. static final int[] loadInsn = { ALOAD, ILOAD, LLOAD, DLOAD, FLOAD };
  935. static final int[] retInsn = { ARETURN, IRETURN, LRETURN, DRETURN,
  936. FRETURN };
  937. static final int[] ldInsn = { ALOAD_0, ILOAD_0, LLOAD_0, DLOAD_0,
  938. FLOAD_0 };
  939. static final int[] stInsn = { ASTORE_0, ISTORE_0, LSTORE_0, DSTORE_0,
  940. FSTORE_0 };
  941. static final int[] storeInsn = { ASTORE, ISTORE, LSTORE, DSTORE, FSTORE };
  942. static final String[] fieldDesc = { D_OBJECT, D_INT, D_LONG, D_DOUBLE,
  943. D_FLOAT };
  944. static final String[] abbrev = { "O", "I", "L", "D", "F" };
  945. static final int[] category = { 1, 1, 2, 2, 1 };
  946. static int toVmType(String type) {
  947. switch (type.charAt(0)) {
  948. case 'Z':
  949. case 'B':
  950. case 'C':
  951. case 'S':
  952. case 'I':
  953. return TINT;
  954. case 'D':
  955. return TDOUBLE;
  956. case 'F':
  957. return TFLOAT;
  958. case 'J':
  959. return TLONG;
  960. case 'N': // null
  961. case 'A': // catch handler return address
  962. case 'L': // normal type
  963. case '[': // array
  964. return TOBJECT;
  965. default:
  966. assert false : "Type " + type + " not handled";
  967. }
  968. return ' ';
  969. }
  970. static void loadVar(MethodVisitor mv, int vmt, int var) {
  971. assert var >= 0 : "Got var = " + var;
  972. // ASM4.1 doesn't like short-form ALOAD_n instructions. Instead, we use ALOAD n.
  973. // if (var < 4) {
  974. // // short instructions like ALOAD_n exist for n = 0 .. 4
  975. // mv.visitInsn(ldInsn[vmt] + var);
  976. // } else {
  977. mv.visitVarInsn(loadInsn[vmt], var);
  978. // }
  979. }
  980. static void storeVar(MethodVisitor mv, int vmt, int var) {
  981. assert var >= 0;
  982. // if (var < 4) {
  983. // // short instructions like ALOAD_n exist for n = 0 .. 4
  984. // mv.visitInsn(stInsn[vmt] + var);
  985. // } else {
  986. mv.visitVarInsn(storeInsn[vmt], var);
  987. // }
  988. }
  989. }