diff options
| author | Cliff Click <cliffc@acm.org> | 2024-07-25 23:46:39 +0300 |
|---|---|---|
| committer | Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com> | 2024-08-05 23:28:33 +0100 |
| commit | 799ba3a8d2603a51fc5b860f7845e72e6af61c38 (patch) | |
| tree | 628fb922e02c3940bda7bbf02da8b86ffca5f637 /chapter12 | |
| parent | e325c3ad27aa12df9f2edfb1988e9c1270a88eea (diff) | |
| download | Simple-799ba3a8d2603a51fc5b860f7845e72e6af61c38.tar.gz Simple-799ba3a8d2603a51fc5b860f7845e72e6af61c38.zip | |
Float bugs
Fix bug infinite loop finding.
Fix inf loops before printing graph.
Missing float unary negate .
Generator makes float exprs
Fix missing dep in LCA finding
Diffstat (limited to 'chapter12')
20 files changed, 113 insertions, 42 deletions
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java index 054a489c..4e0a31d5 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java @@ -10,7 +10,6 @@ public abstract class GlobalCodeMotion { // Node.use(0) is always a block tail (either IfNode or head of the // following block). There are no unreachable infinite loops. public static void buildCFG( StopNode stop ) { - fixLoops(stop); schedEarly(); Parser.SCHEDULED = true; schedLate(Parser.START); @@ -20,7 +19,7 @@ public abstract class GlobalCodeMotion { // Backwards walk on the CFG only, looking for unreachable code - which has // to be an infinite loop. Insert a bogus never-taken exit to Stop, so the // loop becomes reachable. Also, set loop nesting depth - private static void fixLoops(StopNode stop) { + static void fixLoops(StopNode stop) { // Backwards walk from Stop, looking for unreachable code BitSet visit = new BitSet(); HashSet<CFGNode> unreach = new HashSet<>(); @@ -148,7 +147,7 @@ public abstract class GlobalCodeMotion { assert early != null; CFGNode lca = null; for( Node use : n._outputs ) - lca = use_block(n,use, late).idom(lca); + lca = use_block(n,use, late)._idom(lca,null); // Loads may need anti-dependencies, raising their LCA if( n instanceof LoadNode load ) @@ -214,6 +213,7 @@ public abstract class GlobalCodeMotion { break; case LoadNode ld: break; // Loads do not cause anti-deps on other loads case ReturnNode ret: break; // Load must already be ahead of Return + case NeverNode never: break; default: throw Utils.TODO(); } } @@ -226,7 +226,7 @@ public abstract class GlobalCodeMotion { for( ; stblk != defblk.idom(); stblk = stblk.idom() ) { // Store and Load overlap, need anti-dependence if( stblk._anti==load._nid ) { - lca = stblk.idom(lca); // Raise Loads LCA + lca = stblk._idom(lca,null); // Raise Loads LCA if( lca == stblk && st != null && Utils.find(st._inputs,load) == -1 ) // And if something moved, st.addDef(load); // Add anti-dep as well return lca; // Cap this stores' anti-dep to here diff --git a/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java b/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java index b2b76777..121d0233 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -92,6 +92,8 @@ public abstract class IterPeeps { n.kill(); // Just plain dead } + // Break infinite loops, forcing a Never-branch to exit + GlobalCodeMotion.fixLoops(stop); if( show ) System.out.println(new GraphVisualizer().generateDotOutput(stop,null,null)); GlobalCodeMotion.buildCFG(stop); diff --git a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java index 4c62226b..cc1fb0df 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java @@ -429,6 +429,8 @@ public class Parser { throw error("Undefined name '" + name + "'"); _scope.update(name,expr); } + if( expr._type instanceof TypeInteger && t instanceof TypeFloat ) + expr = expr.widen(); if( !expr._type.isa(t) ) throw error("Type " + expr._type.str() + " is not of declared type " + t.str()); return expr; @@ -564,7 +566,7 @@ public class Parser { * @return a unary expression {@link Node}, never {@code null} */ private Node parseUnary() { - if (match("-")) return new MinusNode(parseUnary()).peephole(); + if (match("-")) return new MinusNode(parseUnary()).widen().peephole(); if (match("!")) return new NotNode(parseUnary()).peephole(); return parsePostfix(parsePrimary()); } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java index 4454a5ad..b9faeb32 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -76,8 +76,8 @@ abstract public class BoolNode extends Node { public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(long lhs, long rhs) { return lhs < rhs; } Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } BoolNode copyF() { return new LTF(null,null); }} public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(long lhs, long rhs) { return lhs <= rhs; } Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } BoolNode copyF() { return new LEF(null,null); } } - public static class EQF extends BoolNode { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "=="; } boolean doOp(double lhs, double rhs) { return lhs == rhs; } } - public static class LTF extends BoolNode { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(double lhs, double rhs) { return lhs < rhs; } } - public static class LEF extends BoolNode { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } } + public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } } + public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } } + public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } } } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java index b605071b..07ba0fa4 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -52,16 +52,17 @@ public abstract class CFGNode extends Node { public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } // Return the immediate dominator of this Node and compute dom tree depth. - public CFGNode idom() { return cfg(0); } + public CFGNode idom(Node dep) { return cfg(0); } + public final CFGNode idom() { return idom(null); } // Return the LCA of two idoms - public CFGNode idom(CFGNode rhs) { + public CFGNode _idom(CFGNode rhs, Node dep) { if( rhs==null ) return this; CFGNode lhs = this; while( lhs != rhs ) { var comp = lhs.idepth() - rhs.idepth(); - if( comp >= 0 ) lhs = lhs.idom(); - if( comp <= 0 ) rhs = rhs.idom(); + if( comp >= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom(); + if( comp <= 0 ) rhs = ((CFGNode)rhs.addDep(dep)).idom(); } return lhs; } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java index c662dee6..22862cb9 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -30,7 +30,7 @@ public class LoopNode extends RegionNode { // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) - @Override public CFGNode idom() { return entry(); } + @Override public CFGNode idom(Node dep) { return entry(); } @Override public int loopDepth() { if( _loopDepth!=0 ) return _loopDepth; // Was already set @@ -60,8 +60,11 @@ public class LoopNode extends RegionNode { // directly on the If) we found our exit. CFGNode x = back(); while( x != this ) { - if( x instanceof CProjNode exit ) - return; // Found an exit, not an infinite loop + if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) { + Node other = iff.cproj(1-exit._idx); + if( !(other.out(0) instanceof LoopNode loop) || loop.entry()==exit ) + return; // Found an exit, not an infinite loop + } x = x.idom(); } // Found a no-exit loop. Insert an exit diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java new file mode 100644 index 00000000..0853729d --- /dev/null +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; + +import java.util.BitSet; + +public class MinusFNode extends Node { + public MinusFNode(Node in) { super(null, in); } + + @Override public String label() { return "MinusF"; } + + @Override public String glabel() { return "-"; } + + @Override + StringBuilder _print1(StringBuilder sb, BitSet visited) { + in(1)._print0(sb.append("(-"), visited); + return sb.append(")"); + } + + @Override + public Type compute() { + if (in(1)._type instanceof TypeFloat i0) + return i0.isConstant() ? TypeFloat.constant(-i0.value()) : i0; + return TypeFloat.TOP.meet(in(1)._type); + } + + @Override + public Node idealize() { + // -(-x) is x + if( in(1) instanceof MinusFNode minus ) + return minus.in(1); + + return null; + } +} diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java index 9b687613..ae8e9e1b 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -33,4 +33,5 @@ public class MinusNode extends Node { return null; } + @Override Node copyF() { return new MinusFNode(null); } } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java index 379e140d..ac9c0045 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java @@ -9,4 +9,12 @@ public interface MultiNode extends OutNode { return prj; return null; } + + // Find a projection by index + default CProjNode cproj( int idx ) { + for( Node out : outs() ) + if( out instanceof CProjNode prj && prj._idx==idx ) + return prj; + return null; + } } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java index f50d3503..e77ed653 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java @@ -3,6 +3,8 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.TypeInteger; import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeTuple; + import java.util.BitSet; // "Never true" for infinite loop exits @@ -13,7 +15,7 @@ public class NeverNode extends IfNode { @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); } - @Override public Type compute() { return TypeInteger.BOT; } + @Override public Type compute() { return TypeTuple.IF_BOTH; } @Override public Node idealize() { return null; } } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java index 3af49dbf..03b3d5e0 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java @@ -1,7 +1,6 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.IRPrinter; -import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java index a07dc140..535db980 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -100,7 +100,7 @@ public class PhiNode extends Node { if( in(2)._type == in(2)._type.makeInit() ) nullx = 2; if( nullx != -1 ) { Node val = in(3-nullx); - if( region().idom() instanceof IfNode iff && iff.pred().addDep(this)==val ) { + if( region().idom(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) { // Must walk the idom on the null side to make sure we hit False. CFGNode idom = (CFGNode)region().in(nullx); while( idom.in(0) != iff ) idom = idom.idom(); diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java index dcc7ccef..d5105614 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -99,12 +99,12 @@ public class RegionNode extends CFGNode { return _idepth=d; } - @Override public CFGNode idom() { + @Override public CFGNode idom(Node dep) { CFGNode lca = null; // Walk the LHS & RHS idom trees in parallel until they match, or either fails. // Because this does not cache, it can be linear in the size of the program. for( int i=1; i<nIns(); i++ ) - lca = cfg(i).idom(lca); + lca = cfg(i)._idom(lca,dep); return lca; } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java index 62fc8a6b..5f205ffd 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -75,7 +75,7 @@ public class StartNode extends CFGNode implements MultiNode { // No immediate dominator, and idepth==0 @Override public int idepth() { return 0; } - @Override public CFGNode idom() { return null; } + @Override public CFGNode idom(Node dep) { return null; } @Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) { } diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java index 94b2ad6d..ab567bae 100644 --- a/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ b/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -59,7 +59,7 @@ public class StopNode extends CFGNode { d = Math.max(d,((CFGNode)n).idepth()+1); return _idepth=d; } - @Override public CFGNode idom() { return null; } + @Override public CFGNode idom(Node dep) { return null; } @Override public int loopDepth() { return (_loopDepth=1); } diff --git a/chapter12/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter12/src/test/java/com/seaofnodes/simple/Chapter11Test.java index 9b05d66c..6c1f7938 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -292,7 +292,7 @@ else { } """); StopNode stop = parser.parse(false).iterate(false); - assertEquals("Stop[ return 9; return 0; return 0; ]", stop.toString()); + assertEquals("Stop[ return 9; return 0; return 0; return 0; return 0; ]", stop.toString()); } @@ -433,8 +433,8 @@ while( -arg ) while(1) arg = arg+ptr.v; """); - StopNode stop = parser.parse(false).iterate(false); - assertEquals("return 0;", stop.toString()); + StopNode stop = parser.parse(false).iterate(true); + assertEquals("Stop[ return 0; return 0; ]", stop.toString()); } @Test diff --git a/chapter12/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter12/src/test/java/com/seaofnodes/simple/Chapter12Test.java index bfd6c309..af8fdfe9 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -6,15 +6,27 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class Chapter12Test { @Test + public void testJig() { + Parser parser = new Parser( +""" +return 3.14; +"""); + StopNode stop = parser.parse(false).iterate(false); + assertEquals("return 3.14;", stop.toString()); + assertEquals(3.14, Evaluator.evaluate(stop, 0)); + } + + @Test public void testFloat() { Parser parser = new Parser( """ return 3.14; """); - StopNode stop = parser.parse(false).iterate(true); + StopNode stop = parser.parse(false).iterate(false); assertEquals("return 3.14;", stop.toString()); assertEquals(3.14, Evaluator.evaluate(stop, 0)); } @@ -31,7 +43,7 @@ while( 1 ) { } return guess; """); - StopNode stop = parser.parse(false).iterate(true); + StopNode stop = parser.parse(false).iterate(false); assertEquals("return Phi(Loop9,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)/2.0));", stop.toString()); assertEquals(3.0, Evaluator.evaluate(stop, 9)); assertEquals(1.414213562373095, Evaluator.evaluate(stop, 2)); @@ -44,8 +56,8 @@ return guess; flt x = arg; return x+1==x; """); - StopNode stop = parser.parse(false).iterate(true); - assertEquals("return (((flt)arg+1.0)==ToFloat);", stop.toString()); + StopNode stop = parser.parse(false).iterate(false); + assertEquals("return ((flt)arg==(ToFloat+1.0));", stop.toString()); assertEquals(0L, Evaluator.evaluate(stop, 1)); } diff --git a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 699711fc..efd76481 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -14,7 +14,6 @@ import static org.junit.Assert.assertTrue; */ public class FuzzerWrap { - @Ignore @Test public void fuzzPeeps() { var fuzzer = new Fuzzer(); diff --git a/chapter12/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter12/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java index 09bda290..ab5c9bbd 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java @@ -135,15 +135,16 @@ public class Evaluator { case ConstantNode cons -> cons(cons); case AddNode add -> vall(add.in(1)) + vall(add.in(2)); case AddFNode add -> vald(add.in(1)) + vald(add.in(2)); - case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; - case BoolNode.LE le -> vall(le.in(1)) <= vall(le.in(2)) ? 1L : 0L; - case BoolNode.LT lt -> vall(lt.in(1)) < vall(lt.in(2)) ? 1L : 0L; case BoolNode.EQF eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; case BoolNode.LEF le -> vald(le.in(1)) <= vald(le.in(2)) ? 1L : 0L; case BoolNode.LTF lt -> vald(lt.in(1)) < vald(lt.in(2)) ? 1L : 0L; + case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; + case BoolNode.LE le -> vall(le.in(1)) <= vall(le.in(2)) ? 1L : 0L; + case BoolNode.LT lt -> vall(lt.in(1)) < vall(lt.in(2)) ? 1L : 0L; case DivNode div -> div(div); case DivFNode div -> div(div); case MinusNode minus -> -vall(minus.in(1)); + case MinusFNode minus -> -vald(minus.in(1)); case MulNode mul -> vall(mul.in(1)) * vall(mul.in(2)); case MulFNode mul -> vald(mul.in(1)) * vald(mul.in(2)); case NotNode not -> isTrue(val(not.in(1))) ? 0L : 1L; diff --git a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index 155063ab..053be297 100644 --- a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -90,6 +90,7 @@ public class ScriptGenerator { } private static final Type TYPE_INT = new Type("int"); + private static final Type TYPE_FLT = new Type("flt"); private static class Variable { @@ -343,7 +344,9 @@ public class ScriptGenerator { * @return A random type */ private Type getType() { - if (structs.isEmpty() || random.nextBoolean()) return TYPE_INT; + int t = random.nextInt(10); + if( t<5 || structs.isEmpty() ) return TYPE_INT; + if( t<7 ) return TYPE_FLT; var idx = random.nextInt(structs.size()); TypeStruct struct = structs.get(idx); if (random.nextBoolean()) return struct.nullable; @@ -415,7 +418,7 @@ public class ScriptGenerator { indentation += INDENTATION; for (int i=0; i<fields.length; i++) { var fieldName = getRandomName(); - var type = generateInvalid() ? getType() : TYPE_INT; + var type = generateInvalid() ? getType() : (random.nextBoolean() ? TYPE_INT : TYPE_FLT); printIndentation().append(type.name).append(" ").append(fieldName).append(";\n"); fields[i] = new TypeStruct.Field(fieldName.toString(), type); } @@ -679,16 +682,16 @@ public class ScriptGenerator { */ public void genExpression(Type type) { if (generateInvalid()) type = getType(); - if (type != TYPE_INT) { + if (type != TYPE_INT && type != TYPE_FLT) { genUnary(type); return; } var num = randLog(MAX_BINARY_EXPRESSIONS_PER_EXPRESSION); while(num-->0) { - genUnary(TYPE_INT); + genUnary(type); sb.append(BINARY_OP[random.nextInt(BINARY_OP.length)]); } - genUnary(TYPE_INT); + genUnary(type); } /** @@ -696,7 +699,7 @@ public class ScriptGenerator { */ public void genUnary(Type type) { if (generateInvalid()) type = getType(); - if (type != TYPE_INT) { + if (type != TYPE_INT && type != TYPE_FLT) { genSuffix(type); return; } @@ -704,7 +707,7 @@ public class ScriptGenerator { while(num-->0) { sb.append(UNARY_OP[random.nextInt(UNARY_OP.length)]); } - genSuffix(TYPE_INT); + genSuffix(type); } /** @@ -712,7 +715,7 @@ public class ScriptGenerator { */ public void genSuffix(Type type) { if (generateInvalid()) type = getType(); - if (type == TYPE_INT && random.nextBoolean()) { + if ((type == TYPE_INT || type == TYPE_FLT) && random.nextBoolean()) { var t = getType(); if (generateInvalid()) { var field = getRandomName(); @@ -721,7 +724,7 @@ public class ScriptGenerator { return; } else if (t instanceof TypeStruct s && s.fields.length > 0) { var field = s.fields[random.nextInt(s.fields.length)]; - if (field.type == TYPE_INT) { + if (field.type == TYPE_INT || field.type == TYPE_FLT) { genPrimary(t); sb.append(".").append(field.name); return; @@ -762,6 +765,8 @@ public class ScriptGenerator { case 1 -> sb.append("false"); default -> sb.append(random.nextInt(1<<(rand-2))); } + } else if (type == TYPE_FLT) { + sb.append(random.nextFloat()); } else if (type instanceof TypeNullable n) { if (random.nextBoolean()) { sb.append("null"); |
