From 1cb8279a64f4f79615ac8358927b11237df29bf2 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Wed, 12 Aug 2026 11:20:14 -0700 Subject: Real argc/argv args Beef up BubbleSort error handling --- .../main/java/com/seaofnodes/simple/Parser.java | 37 +++++++++++++++------- .../com/seaofnodes/simple/codegen/ElfWriter.java | 19 +++++++---- .../simple/codegen/GlobalCodeMotion.java | 30 ++++++++++++------ 3 files changed, 58 insertions(+), 28 deletions(-) (limited to 'chapter25/src/main/java/com/seaofnodes') diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java index 4c2dfc84..e964fa5b 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -1450,8 +1450,10 @@ public class Parser { // Make an array type of t. Always record a mutable version, // but return the requested version. private TypeStruct typeAry( Type t, boolean efinal ) { - if( t instanceof TypeMemPtr tmp && tmp.notNull() ) - throw error("Arrays of reference types must always be nullable"); + // Non-null reference arrays are valid types, even though an ordinary + // zero-filled `new T[len]` cannot construct one. Allocation retains + // that check in alloc(); trusted runtimes (notably argv construction) + // may fill the private array completely before publishing it. String tname = ("[]"+t.str()).intern(); TypeStruct ta = (TypeStruct)TYPES.get(tname); if( ta==null ) { @@ -2406,22 +2408,33 @@ public class Parser { } if( _lexer.isEOF() ) throw error("Unclosed string"); - char esc = _lexer.nextChar(); - sb.append(switch( esc ) { - case 'n' -> '\n'; - case 't' -> '\t'; - case 'r' -> '\r'; - case '\\' -> '\\'; - case '"' -> '"'; - default -> throw error("Unknown string escape \\\\"+esc); - }); + sb.append(escapedChar("string")); } throw error("Unclosed string"); } + private char escapedChar(String kind) { + char esc = _lexer.nextChar(); + return switch( esc ) { + case 'n' -> '\n'; + case 't' -> '\t'; + case 'r' -> '\r'; + case '\\' -> '\\'; + case '"' -> '"'; + case '\'' -> '\''; + default -> throw error("Unknown "+kind+" escape \\\\"+esc); + }; + } + // Already parsed "'" private Node parseChar() { - return require(con(TypeInteger.constant(_lexer.nextChar())),"'"); + if( _lexer.isEOF() ) throw error("Unclosed character"); + char c = _lexer.nextChar(); + if( c=='\\' ) { + if( _lexer.isEOF() ) throw error("Unclosed character"); + c = escapedChar("character"); + } + return require(con(TypeInteger.constant(c)),"'"); } private boolean noLowerCase(String s) { diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java index 508cdcca..274be838 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java @@ -182,7 +182,7 @@ public class ElfWriter { // creates function and stores where it starts. // Filters to just this class.obj file. - void encodeFunctions(StopNode stop, Encoding enc, int text_idx) { + void encodeFunctions(StopNode stop, Encoding enc, int text_idx, boolean emitEntrySymbol) { for( Node stopcu : stop._inputs ) { for( Node ret : stopcu._inputs ) { FunNode fun = ((ReturnNode)ret).fun(); @@ -191,8 +191,15 @@ public class ElfWriter { int end = enc.opStart(fun.ret()) + enc.opLen(fun.ret()); long value = enc.opStart(fun); long size = end - value; - if( fun._name != null ) // Anonymous functions have no name - symbol(fun._name, text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, value, size); + if( fun._name != null ) { // Anonymous functions have no name + // The C runtime owns `main` and adapts argc/argv into + // a normal Simple array. Keep the Simple function on + // its native ABI under a private entry symbol. + String name = emitEntrySymbol && "main".equals(fun._name) + ? "simple_main" + : fun._name; + symbol(name, text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, value, size); + } } } } @@ -205,14 +212,14 @@ public class ElfWriter { for( FunNode fun : _code._linker ) if( fun != null && _code.owns(fun) ) { if( "main".equals(fun._name) ) - return; // Top-level declared a main, take the existing one + return; // encodeFunctions exported this as simple_main if( entryName.equals(fun._name) ) clinit = fun; // Keep top-level entry, if we don't find an explicit main } // No top-level main, so the top-level clinit becomes a default main if( clinit != null ) { assert enc.opStart(clinit) == 0; - symbol("main",text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, enc.opStart(clinit), 0); + symbol("simple_main",text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, enc.opStart(clinit), 0); return; } throw new IllegalStateException("Missing entry "+entryName); @@ -293,7 +300,7 @@ public class ElfWriter { // populate function symbols - symbols.encodeFunctions(_code._stop, _code._encoding, text._index); + symbols.encodeFunctions(_code._stop, _code._encoding, text._index, emitEntrySymbol); // Export the source compilation unit as the C runtime "main". // Encoding keeps this function at text offset zero. if( emitEntrySymbol ) diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index 5d8ed03c..fa187000 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -63,6 +63,13 @@ public abstract class GlobalCodeMotion { private static void breakUpGlobalConstantSingle( Node con ) { + // If an immediate global-value user still spans functions, it cannot + // yet be assigned to one clone. Keeping the constant at Start is a + // valid schedule; a downstream value with concrete ownership can be + // split independently. + for( Node use : con.outs() ) + if( use != null && useFun(use)==null ) + return; // While constant has users in different functions while( true ) { // Find a function user, and another function @@ -106,26 +113,29 @@ public abstract class GlobalCodeMotion { } private static FunNode useFun(Node use) { + return useFun(use,new BitSet()); + } + + private static FunNode useFun(Node use, BitSet visit) { + if( visit.get(use._nid) ) return null; + visit.set(use._nid); if( use instanceof ReturnNode ret ) return ret.fun(); if( use instanceof ParmNode parm ) return parm.fun(); CFGNode cfg = use.cfg0(); - if( cfg==null ) return null; - FunNode fun = cfg.fun(); + FunNode fun = cfg==null ? null : cfg.fun(); // Use itself is not in a function... which makes it a 2-part // constant such as happens on some chips where large constants have // to be built up in parts. PtrToInt followed by the array-body Add is // another zero-code/global-value chain, so walk through any number of // single-use pieces until reaching the function-owned use. - if( fun==null ) { - do { - assert use.nOuts()==1; - use = use.out(0); - cfg = use.cfg0(); - fun = cfg==null ? null : cfg.fun(); - } while( fun==null ); - } + if( fun==null ) + for( Node out : use.outs() ) { + FunNode f = useFun(out,(BitSet)visit.clone()); + if( f != null && fun != null && f != fun ) return null; + if( f != null ) fun = f; + } return fun; } -- cgit v1.2.3