diff options
| author | Cliff Click <cliffc@acm.org> | 2025-06-13 15:42:08 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-24 15:56:44 -0700 |
| commit | f5f0580331b4e29a0a0dc43d9885dfe0f0aed865 (patch) | |
| tree | a983bd53ce8173b21380f0b3bf06d88d845ae861 | |
| parent | b59f51486393b2fae7b0015bc5099c5514c7335a (diff) | |
| download | Simple-f5f0580331b4e29a0a0dc43d9885dfe0f0aed865.tar.gz Simple-f5f0580331b4e29a0a0dc43d9885dfe0f0aed865.zip | |
Chapter 25: SSA Before Types Are Known
Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com>
Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com>
Co-authored-by: Symious <14933944+symious@users.noreply.github.com>
Co-authored-by: Jun Kawasaki <jun784@gmail.com>
Co-authored-by: Hadházy Tamás <helsgithub15@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
99 files changed, 3205 insertions, 411 deletions
diff --git a/README.ja.md b/README.ja.md index 596a8354..bd16eb1a 100644 --- a/README.ja.md +++ b/README.ja.md @@ -63,7 +63,7 @@ Simple 言語のスタイルは、C または Java のサブセットに倣っã� * [第23章](docs/chapter23/README.ja.md):メソッドと型の再検討。 * [第24a章](docs/chapter24a/README.ja.md):関数内の SCCP * [第24b章](docs/chapter24b/README.ja.md):関数間の SCCP -* 第25章:型が決まる前の SSA +* [第25章](docs/chapter25/README.ja.md):型が決まる前の SSA * 第26章:構築とプライベートメモリ * 第27章:エスケープ解析と未知の呼び出し元 * 第28a章:モジュールと遅延クラス初期化 @@ -72,7 +72,7 @@ The Simple language is styled after a subset of C or Java. * [Chapter 23](docs/chapter23/README.md): Methods and Types Revisited * [Chapter 24a](docs/chapter24a/README.md): SCCP Within a Function * [Chapter 24b](docs/chapter24b/README.md): SCCP Across Functions -* Chapter 25: SSA Before Types Are Known +* [Chapter 25](docs/chapter25/README.md): SSA Before Types Are Known * Chapter 26: Construction and Private Memory * Chapter 27: Escape Analysis and Unknown Callers * Chapter 28a: Modules and Lazy Class Initialization diff --git a/docs/chapter25/24-grammar.ja.md b/docs/chapter25/24-grammar.ja.md new file mode 100644 index 00000000..fd3fa232 --- /dev/null +++ b/docs/chapter25/24-grammar.ja.md @@ -0,0 +1,168 @@ +# 第24章の文法 + +[English](24-grammar.md) | 日本語 + +```antlrv4 +grammar SimpleLanguage; + +// Name lookup, assignable operands, type compatibility, and access permissions +// are checked by the compiler in addition to these syntax rules. +program : statement* EOF ; + +block : '{' statement* '}' ; + +statement + : block + | returnStatement + | ifStatement + | whileStatement + | forStatement + | breakStatement + | continueStatement + | structDeclaration + | declaration ';' + | assignment ';' + | ';' + ; + +returnStatement : 'return' assignment ';' ; +ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ; +whileStatement : 'while' '(' assignment ')' statement ; +breakStatement : 'break' ';' ; +continueStatement : 'continue' ';' ; + +forStatement + : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement + ; + +structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ; +structMember : methodDeclaration | statement ; + +// A method is an ordinary fixed function binding in a struct definition. +// The compiler supplies self; it is not written in parameterList. +// The general declaration rule also admits explicitly typed function fields. +methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ; + +declaration : declarationType binding (',' binding)* ; +declarationType : type | 'var' | 'val' ; +binding : bindingModifier? IDENTIFIER ('=' initializer)? ; +bindingModifier : '!' | '~' ; +// A declaration initializer consisting of the string "C" declares an extern +// symbol. Ordinary strings elsewhere are array-valued expressions. +initializer : assignment ; + +// The prefix qualifies the named struct reference, not an enclosing array. +// Each [] / [~] suffix qualifies its own array layer. ? requires a reference. +type + : functionType + | primitiveType typeSuffix* + | accessModifier? typeName typeSuffix* + ; +accessModifier : '!' | '~' ; +typeSuffix : '?' | '[]' | '[~]' ; +typeName : IDENTIFIER ('.' IDENTIFIER)* ; + +primitiveType + : 'int' | 'i8' | 'i16' | 'i32' | 'i64' + | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool' + | 'flt' | 'f32' | 'f64' + ; + +assignment : expression (assignmentOperator assignment)? ; +assignmentOperator + : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' + | '<<=' | '>>=' | '>>>=' + ; + +expression : logicalExpression ('?' assignment (':' assignment)?)? ; + +// Both logical operators short-circuit and associate to the right here. +logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ; + +bitwiseExpression + : equalityExpression (('&' | '|' | '^') equalityExpression)* + ; + +equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ; + +// Chained comparisons test adjacent operands and short-circuit. A chain must +// keep its direction; equality has lower precedence and does not join a chain. +comparisonExpression + : shiftExpression + ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )? + ; + +shiftExpression + : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (('+' | '-') multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (('*' | '/') unaryExpression)* + ; + +unaryExpression + : ('-' | '!') unaryExpression + | updateOperator IDENTIFIER + | postfixExpression + ; + +// Prefix updates require a local name; postfix updates also allow fields +// and array elements. All updates require an assignable operand. +updateOperator : '++' | '--' ; +postfixExpression : primaryExpression postfixSuffix* updateOperator? ; +postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ; + +// For a fixed function-valued member, object.name(args) passes object as self. +// This overlaps ordinary field selection followed by a call syntactically; +// name/type resolution determines which call form applies. +methodCallSuffix : '.' IDENTIFIER callSuffix ; + +primaryExpression + : INTEGER_LITERAL + | FLOAT_LITERAL + | STRING_LITERAL + | CHARACTER_LITERAL + | 'true' + | 'false' + | 'null' + | IDENTIFIER + | '(' assignment ')' + | newExpression + | functionExpression + ; + +// A function is an expression bound with an ordinary declaration. +// Its last statement supplies the implicit result; return can exit earlier. +functionExpression : '{' parameterList? '->' statement* '}' ; +parameterList : (parameter ','?)+ ; +parameter : type bindingModifier? IDENTIFIER ; + +// Function types have unnamed, space-separated argument types. +// {int} is the zero-argument type; its function expression still uses ->. +functionType + : '{' type+ '->' type '}' '?'? + | '{' type '}' '?'? + ; + +callSuffix : '(' argumentList? ')' ; +argumentList : assignment (',' assignment)* ','? ; + +// Struct initialization uses a block; array allocation supplies a length. +newExpression : 'new' type ('[' assignment ']' | block)? ; + +INTEGER_LITERAL : '0' | [1-9] DIGIT* ; +FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ; +STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ; +CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ; +fragment ESCAPE : '\\' [0ntr\\'"] ; +IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ; +fragment NON_DIGIT : [a-zA-Z_] ; +fragment DIGIT : [0-9] ; +WHITESPACE : [ \t\r\n]+ -> skip ; +LINE_COMMENT : '//' ~[\r\n]* -> skip ; +BLOCK_COMMENT : '/*' .*? '*/' -> skip ; +``` diff --git a/docs/chapter25/24-grammar.md b/docs/chapter25/24-grammar.md new file mode 100644 index 00000000..22867355 --- /dev/null +++ b/docs/chapter25/24-grammar.md @@ -0,0 +1,168 @@ +# Grammar for Chapter 24 + +English | [日本語](24-grammar.ja.md) + +```antlrv4 +grammar SimpleLanguage; + +// Name lookup, assignable operands, type compatibility, and access permissions +// are checked by the compiler in addition to these syntax rules. +program : statement* EOF ; + +block : '{' statement* '}' ; + +statement + : block + | returnStatement + | ifStatement + | whileStatement + | forStatement + | breakStatement + | continueStatement + | structDeclaration + | declaration ';' + | assignment ';' + | ';' + ; + +returnStatement : 'return' assignment ';' ; +ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ; +whileStatement : 'while' '(' assignment ')' statement ; +breakStatement : 'break' ';' ; +continueStatement : 'continue' ';' ; + +forStatement + : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement + ; + +structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ; +structMember : methodDeclaration | statement ; + +// A method is an ordinary fixed function binding in a struct definition. +// The compiler supplies self; it is not written in parameterList. +// The general declaration rule also admits explicitly typed function fields. +methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ; + +declaration : declarationType binding (',' binding)* ; +declarationType : type | 'var' | 'val' ; +binding : bindingModifier? IDENTIFIER ('=' initializer)? ; +bindingModifier : '!' | '~' ; +// A declaration initializer consisting of the string "C" declares an extern +// symbol. Ordinary strings elsewhere are array-valued expressions. +initializer : assignment ; + +// The prefix qualifies the named struct reference, not an enclosing array. +// Each [] / [~] suffix qualifies its own array layer. ? requires a reference. +type + : functionType + | primitiveType typeSuffix* + | accessModifier? typeName typeSuffix* + ; +accessModifier : '!' | '~' ; +typeSuffix : '?' | '[]' | '[~]' ; +typeName : IDENTIFIER ('.' IDENTIFIER)* ; + +primitiveType + : 'int' | 'i8' | 'i16' | 'i32' | 'i64' + | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool' + | 'flt' | 'f32' | 'f64' + ; + +assignment : expression (assignmentOperator assignment)? ; +assignmentOperator + : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' + | '<<=' | '>>=' | '>>>=' + ; + +expression : logicalExpression ('?' assignment (':' assignment)?)? ; + +// Both logical operators short-circuit and associate to the right here. +logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ; + +bitwiseExpression + : equalityExpression (('&' | '|' | '^') equalityExpression)* + ; + +equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ; + +// Chained comparisons test adjacent operands and short-circuit. A chain must +// keep its direction; equality has lower precedence and does not join a chain. +comparisonExpression + : shiftExpression + ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )? + ; + +shiftExpression + : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (('+' | '-') multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (('*' | '/') unaryExpression)* + ; + +unaryExpression + : ('-' | '!') unaryExpression + | updateOperator IDENTIFIER + | postfixExpression + ; + +// Prefix updates require a local name; postfix updates also allow fields +// and array elements. All updates require an assignable operand. +updateOperator : '++' | '--' ; +postfixExpression : primaryExpression postfixSuffix* updateOperator? ; +postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ; + +// For a fixed function-valued member, object.name(args) passes object as self. +// This overlaps ordinary field selection followed by a call syntactically; +// name/type resolution determines which call form applies. +methodCallSuffix : '.' IDENTIFIER callSuffix ; + +primaryExpression + : INTEGER_LITERAL + | FLOAT_LITERAL + | STRING_LITERAL + | CHARACTER_LITERAL + | 'true' + | 'false' + | 'null' + | IDENTIFIER + | '(' assignment ')' + | newExpression + | functionExpression + ; + +// A function is an expression bound with an ordinary declaration. +// Its last statement supplies the implicit result; return can exit earlier. +functionExpression : '{' parameterList? '->' statement* '}' ; +parameterList : (parameter ','?)+ ; +parameter : type bindingModifier? IDENTIFIER ; + +// Function types have unnamed, space-separated argument types. +// {int} is the zero-argument type; its function expression still uses ->. +functionType + : '{' type+ '->' type '}' '?'? + | '{' type '}' '?'? + ; + +callSuffix : '(' argumentList? ')' ; +argumentList : assignment (',' assignment)* ','? ; + +// Struct initialization uses a block; array allocation supplies a length. +newExpression : 'new' type ('[' assignment ']' | block)? ; + +INTEGER_LITERAL : '0' | [1-9] DIGIT* ; +FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ; +STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ; +CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ; +fragment ESCAPE : '\\' [0ntr\\'"] ; +IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ; +fragment NON_DIGIT : [a-zA-Z_] ; +fragment DIGIT : [0-9] ; +WHITESPACE : [ \t\r\n]+ -> skip ; +LINE_COMMENT : '//' ~[\r\n]* -> skip ; +BLOCK_COMMENT : '/*' .*? '*/' -> skip ; +``` diff --git a/docs/chapter25/Makefile b/docs/chapter25/Makefile new file mode 100644 index 00000000..12659b8f --- /dev/null +++ b/docs/chapter25/Makefile @@ -0,0 +1,12 @@ +GV := $(wildcard *.gv) +SVG := $(patsubst %.gv,%.svg,$(GV)) + +.PHONY: all +all: $(SVG) + +%.svg: %.gv + dot -Tsvg $< > $@ + +.PHONY: clean +clean: + @rm -f $(SVG) diff --git a/docs/chapter25/README.ja.md b/docs/chapter25/README.ja.md new file mode 100644 index 00000000..8eaef623 --- /dev/null +++ b/docs/chapter25/README.ja.md @@ -0,0 +1,17 @@ +# 第25章: 型が確定する前の SSA 構築 + +[English](README.md) | 日本語 + +[Previous: Chapter 24b](../chapter24b/README.ja.md) | +[Next: Chapter 26](../chapter26/README.ja.md) + +この章では、前方参照やループのために型がまだ分からない場合でも、演算と +フィールド参照をグラフに記録します。数値演算のモード、Convert、Guard、 +シンボルによるフィールド位置を使い、型と配置が確定した後に解決します。 +値の Phi と Load は、パーサが与える仮の型の下限を使いません。 + +コンストラクタは前章の方式を維持します。初期化用の呼び出し、非公開の +メモリ、オブジェクトの公開は次の章で扱います。詳しい説明と例は +[英語版](README.md)を参照してください。 + +このディレクトリで `make lib tests CTAGS=` を実行できます。 diff --git a/docs/chapter25/README.md b/docs/chapter25/README.md new file mode 100644 index 00000000..1dda8928 --- /dev/null +++ b/docs/chapter25/README.md @@ -0,0 +1,151 @@ +# Chapter 25: SSA Before Types Are Known + +English | [日本語](README.ja.md) + +[Previous: Chapter 24b](../chapter24b/README.md) | +[Next: Chapter 26](../chapter26/README.md) + +The previous compiler solved for recursive types and calls, but its parser +still needs types to choose e.g. float-vs-integer arithmetic instructions or +field types. Forward references, external references, or even simple loop +structures can hide what's needed. + +This chapter records the operations first and resolves their type-dependent +parts later. Scalar types, numeric modes, conversions, guards, symbolic +fields, and unbounded value Phis and Loads have to get typed *together*, as +they all have mutually interlocking type dependencies. + + +## A definition can follow its use + +```simple +val add = { Later p -> return p.x + 1; }; +struct Later { flt x; }; +return add(new Later { x = 2.5; }); +``` + +When parsing `p.x + 1`, the compiler knows the receiver's name (type `Later`), +but not its fields! It makes a Load with a symbolic field offset `FldOff` and +an `Add` whose numeric mode is still unset: might be an integer add or a +floating-point add. Later we parse `Later`, which supplies the field and its +storage type. Type propagation then selects floating-point addition and +converts the integer `1` operand. The result is some floating point math +`2.5 + (flt)1`, which folds to `3.5`. + +Forward writes work the same way. Until a field is known, its Store carries +whole memory - it aliases everything until we know what it aliases. Once +resolved, the Store converts its value to the declared field type and updates +that field's alias, which generally will result in a cascade of peepholes +sharpening the memory edges. Resolving the alias preserves the Store's control +input. Update operators such as `x += y` work like `x = x + y`, while evaluating +the destination only once. Storing into a `u8` field may narrow an integer +result; storing into an `f64` field may instead convert it to floating point. + +`FldOff` deserves more mention: it represents a named field reference (Load or +Store) to an incomplete `TypeStruct`. In the example above, that's `p.x`: field +`x` of a `Later` whose fields are not yet declared. The struct can gain more +fields, and the field type itself is still unknown, so +`FldOff` really knows very little about what's going on. It's more of a *shape* +placeholder, and awaits stronger types to peephole into a "real" field +reference. + + +## Values need their own bounds + +The parser always knows when it has *values* vs memory or control, but maybe +not whether something is an int or a flt until much later. Instead it uses +`TypeScalar` for this. `TypeScalar` contains integers, floats, object +pointers, and function pointers. These are classic *values* in the sense that +they represent small concrete things that typically fit in a machine register. +Values exclude control and memory. Scalar bottom says that a value's family is +not known; its top represents an unreachable scalar result. A scalar union can +still be tested for truth, even when its alternatives use different families; +integers, floats, and pointers all carry a notion of zero. + +A scalar Phi now meets its live inputs; it no longer carries a parser-supplied +lower bound. An unfinished loop Phi starts at scalar bottom because its +backedge is missing. Parameters retain their declared interface types, and +memory Phis retain their memory role. Loop Phis, Parms, and recursive CallEnds +still do widening. + + +## Record conversions and branch facts + +The parser ends up inserting `Convert` nodes where it used to directly convert +values. This includes converting integers to match float operators and narrowing +values stored into integer fields. Loads derive their value types from memory; +they no longer impose a parser-supplied bound. A bad load or store still produces +bottom. Type checking looks through a conversion with an already-broken input +so it can report the original error. + +`Convert` records a known destination type, such as a local declaration or +function parameter. It waits for its input type to sharpen, and then peepholes +into a widening, truncation, permission check, or no operation. + +`Guard` records a branch fact: zero/null or nonzero/non-null. Its value family +comes from its input when known. The control edge keeps the fact on the path +where it was proved. A guard output is the same runtime value as its input; it +emits no instruction, just the sharpened type result. Guards are typically +used to track not-null pointer tests across large areas. + +Arithmetic nodes carry a `mode`: a 3-state enum tracking an unset, integer, or +floating-point mode. Known inputs can settle it during peepholes. SCCP +resolves remaining recursive numeric components, then propagates the result +again. Once the mode is known, the machine instruction selection sections will +pick the correct machine ops. + + +## Complete declarations, then update the graph + +```simple +val foo = { A a -> a.b; }; +struct A { B b; }; +``` + +When parsing `foo`, we know that `a` points to an `A`, but not the fields of +`A` or the type of `a.b`. The load records that `A` must have a field named `b`. +Forward shapes (`A`) remain *open* until their declaration, as in prior +chapters. When parsing `struct A` we find `B`, but `B` here is never declared... and +does not need to be. + +After parsing, the compiler *closes* all declarations as a group, including +type cycles (such as a `struct LinkedList` having a `next` field typed as a +`LinkedList`) and references through function signatures. It then replaces old +type shapes in cached node types and stored declarations, preserving pointer +permissions and nullability. Declared types now have their complete field sets. +An undeclared type such as `B` may remain opaque: this example only passes its +pointer around and does not need to allocate a `B` or access its fields. + +Closing declarations does not itself prove the program will type-check. + +At this point `FldOff` can begin to peephole into a `ConFldOff` - moving +from a symbolic field reference of an incomplete type to a concrete +field of a known type. Later, after structures get a *layout*, `ConFldOff` +will become a concrete field offset, a number like `4` or `8`. + + +## The construction boundary + +This snapshot keeps the preceding chapter's constructor model: collect field +values in the constructor scope, check completion, then allocate and initialize. +An unknown expression type is no longer evidence of an unwritten field, so the +completion check follows explicit missing-initialization values through merges. + +Allocation helpers, hidden initializer calls, private self-memory, and explicit +publication belong to the next chapter. Escape discovery, source modules, lazy +class initialization, and serialized ideal IR follow later. These changes use +the existing conservative policy for functions exposed to unknown callers. +Method binding retains the preceding parser's requirement that the owner's +field declaration be known. Forward data fields are resolved here; moving +method and initializer bodies out of that parser scope is construction work. + +## Building and checking + +Run `make lib tests CTAGS=` here, or from the repository root run +`make tests CHAPTERS=chapter25 CTAGS=`. + +`IncompleteTypesTest` exercises a forward field with deferred numeric mode and +a forward store whose byte width must be resolved. The inherited suite covers +recursive objects, null guards, initialization errors, loops, all three code +generators, and native calls. Earlier parse-only graph expectations now show +conversions that disappear after type propagation. diff --git a/docs/chapter25/examples/A_helloWorld.smp b/docs/chapter25/examples/A_helloWorld.smp new file mode 100644 index 00000000..07ff935f --- /dev/null +++ b/docs/chapter25/examples/A_helloWorld.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +sys.io.p("Hello, World!"); +return 0; diff --git a/docs/chapter25/examples/BubbleSort.smp b/docs/chapter25/examples/BubbleSort.smp new file mode 100644 index 00000000..938e3181 --- /dev/null +++ b/docs/chapter25/examples/BubbleSort.smp @@ -0,0 +1,37 @@ +// -*- mode: java; -*- + +// An implementation of +// https://sampleprograms.io/projects/bubble-sort/ +// +// Bubble sort, with reading (parsing) and writing (formatting) the data. + +val sort = { !sys.aryi64 nums -> + var done = false; + while( !done ) { + done = true; + for( var i = 0; i < nums.len-1; i++ ) + if( nums.buf[i] > nums.buf[i+1] ) { + int tmp = nums.buf[i]; + nums.buf[i] = nums.buf[i+1]; + nums.buf[i+1] = tmp; // Swap + done = false; + } + } +}; + +// Read from stdin a line of the form "[ 4, 3, 2, 1]" and print it sorted. +val main = { -> + // Parse array of numbers from stdin + var nums = sys.parseAryI64( sys.io.stdin() ); + if( !nums ) { + sys.io.p("stdin must look like [4, 3, 2, 1]"); + return 1; + } + + // Bubble sort array of integers + sort(nums); + + // Print a list of numbers to stdout + nums.str().write(1); + return 0; +}; diff --git a/docs/chapter25/font-awesome-4.7.0.zip b/docs/chapter25/font-awesome-4.7.0.zip Binary files differnew file mode 100644 index 00000000..b6bdb4bd --- /dev/null +++ b/docs/chapter25/font-awesome-4.7.0.zip diff --git a/docs/chapter25/ifg_trim.pdf b/docs/chapter25/ifg_trim.pdf Binary files differnew file mode 100644 index 00000000..d952fd4f --- /dev/null +++ b/docs/chapter25/ifg_trim.pdf diff --git a/docs/chapter25/lattice.gv b/docs/chapter25/lattice.gv new file mode 100644 index 00000000..8661082d --- /dev/null +++ b/docs/chapter25/lattice.gv @@ -0,0 +1,203 @@ +digraph Lattice { + compound = true; + // Define nodes + splines=false; + node [shape=circle, style=filled]; + nodesep=0.2; + ranksep=0.2; + + TOP [label=<<B>⊤</B >>, fillcolor=black, fontcolor=white]; + BOT [label=<<B>⊥</B>>, fillcolor=black, fontcolor=white]; + + // ---------------------- + // Control + XCTRL [label=<<B>⊤</B >:ctrl>, fillcolor=yellow, shape=oval]; + CTRL [label=<<B>⊥</B>:ctrl>, fillcolor=yellow, shape=oval]; + + TOP -> XCTRL; + XCTRL -> CTRL; + CTRL -> BOT; + + // ---------------------- + // Integers + INT_TOP [label=<<B>⊤</B >:int>, fillcolor=lightblue, shape=oval]; + INT_BOT [label=<<B>⊥</B>:int>, fillcolor=lightblue, shape=oval]; + + INT_HI [label="[min>max]" , fillcolor=lightblue, shape=oval]; + INT_XBOOL [label="[1...0]", fillcolor=lightblue, shape=oval]; + INT_CON [label="constants", fillcolor=lightblue, shape=oval]; + INT_CON0 [label="0", fillcolor=lightblue]; + INT_CON1 [label="1", fillcolor=lightblue]; + INT_BOOL [label="[0...1]", fillcolor=lightblue,shape=oval]; + INT_LO [label="[min<max]" , fillcolor=lightblue, shape=oval]; + + TOP -> INT_TOP; + INT_TOP -> INT_HI; + INT_HI -> INT_XBOOL; + INT_XBOOL -> INT_CON0; + INT_XBOOL -> INT_CON1; + INT_HI -> INT_CON; + INT_CON0 -> INT_BOOL; + INT_CON1 -> INT_BOOL; + INT_BOOL -> INT_LO; + INT_CON -> INT_LO; + INT_LO -> INT_BOT; + INT_BOT -> BOT; + + INT_CON0 -> INT_CON1 -> INT_CON [style=invis]; + + // ---------------------- + // Floats + FLT_TOP [label=<<B>⊤</B >:flt>, fillcolor=aqua, shape=oval]; + FLT_BOT [label=<<B>⊥</B>:flt>, fillcolor=aqua, shape=oval]; + + FLT_CON64 [label="64b consts\n[excludes\n32b consts]" , fillcolor=aqua, shape=oval]; + FLT_CON32 [label="32b consts", fillcolor=aqua, shape=oval]; + FLT_CONPI [label="3.14159...", fillcolor=aqua, shape=oval]; + FLT_X32 [label="~f32", fillcolor=aqua]; + FLT_CON0 [label="0." , fillcolor=aqua]; + FLT_32 [label="f32" , fillcolor=aqua]; + + TOP -> FLT_TOP; + FLT_TOP -> FLT_CON64 + FLT_TOP -> FLT_CONPI + FLT_CON64 -> FLT_BOT + FLT_CONPI -> FLT_BOT + + FLT_TOP -> FLT_X32; + FLT_X32 -> FLT_CON0; + FLT_X32 -> FLT_CON32; + FLT_CON0 -> FLT_32; + FLT_CON32 -> FLT_32; + FLT_32 -> FLT_BOT + + FLT_BOT -> BOT; + + FLT_CON0 -> FLT_CON32 -> FLT_CONPI -> FLT_CON64 [style=invis]; + + + // ---------------------- + // Pointers, just a boolean nil/not and a struct + PTR [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="green"><FONT color="white">*ptr</FONT></TD></TR> + <TR><TD BGCOLOR="lightgreen">struct</TD></TR> + </TABLE> + >]; + PTR0 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="green"><FONT color="white">*ptr?</FONT></TD></TR> + <TR><TD BGCOLOR="lightgreen">struct</TD></TR> + </TABLE> + >]; + + TOP -> PTR; + PTR -> PTR0; + PTR0 -> BOT; + + // ---------------------- + // Structs + STRUCT_TOP [label=<<B>⊤</B >:struct>, fillcolor=lightgreen, shape=oval]; + STRUCT_BOT [label=<<B>⊥</B>:struct>, fillcolor=lightgreen, shape=oval]; + + STRUCT_S1 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + STRUCT_S2 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + STRUCT_SN [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">... </TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + + + TOP -> STRUCT_TOP; + STRUCT_TOP -> STRUCT_S1; + STRUCT_TOP -> STRUCT_S2; + STRUCT_TOP -> STRUCT_SN; + STRUCT_S1 -> STRUCT_BOT; + STRUCT_S2 -> STRUCT_BOT; + STRUCT_SN -> STRUCT_BOT; + STRUCT_BOT -> BOT; + + STRUCT_S1 -> STRUCT_S2 -> STRUCT_SN [style=invis]; + + // ---------------------- + // Memory + MEM_TOP [label=<<B>⊤</B >:mem>, fillcolor=blue, fontcolor=white, shape=oval]; + MEM_BOT [label=<<B>⊥</B>:mem>, fillcolor=blue, fontcolor=white, shape=oval]; + MEM_ALIAS1 [label="#1" , fillcolor=blue, fontcolor=white]; + MEM_ALIASDOTS [label="..." , fillcolor=blue, fontcolor=white, shape=oval]; + MEM_ALIASN [label="#n" , fillcolor=blue, fontcolor=white]; + + TOP -> MEM_TOP; + MEM_TOP -> MEM_ALIAS1; + MEM_TOP -> MEM_ALIASDOTS; + MEM_TOP -> MEM_ALIASN; + MEM_ALIAS1 -> MEM_BOT; + MEM_ALIASDOTS -> MEM_BOT; + MEM_ALIASN -> MEM_BOT; + MEM_BOT -> BOT; + + MEM_ALIAS1 -> MEM_ALIASDOTS -> MEM_ALIASN [style=invis]; + + // ---------------------- + // Tuples + + TUPLE_TOP [label=<<B>⊤</B >:tuple>, fillcolor=pink, shape=oval]; + TUPLE_BOT [label=<<B>⊥</B>:tuple>, fillcolor=pink, shape=oval]; + + TUPLE1 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + TUPLE2 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + TUPLEN [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">...</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + + + TOP -> TUPLE_TOP; + TUPLE_TOP -> TUPLE1; + TUPLE_TOP -> TUPLE2; + TUPLE_TOP -> TUPLEN; + TUPLE1 -> TUPLE_BOT; + TUPLE2 -> TUPLE_BOT; + TUPLEN -> TUPLE_BOT; + TUPLE_BOT -> BOT; + + // ---------------------- + // Additional customization + {rank=same; TOP;} + {rank=same; INT_TOP; FLT_TOP; XCTRL; PTR; STRUCT_TOP; MEM_TOP; TUPLE_TOP} + {rank=same; INT_CON; INT_CON0; INT_CON1; + FLT_CON32; FLT_CON64; FLT_CON0; FLT_CONPI; + STRUCT_S1; STRUCT_S2; STRUCT_SN; + MEM_ALIAS1; MEM_ALIASDOTS; MEM_ALIASN; + TUPLE1; TUPLE2; TUPLEN; + } + {rank=same; INT_BOT; FLT_BOT; CTRL; PTR0; STRUCT_BOT; MEM_BOT; TUPLE_BOT} + {rank=same; BOT;} +} diff --git a/docs/chapter25/lattice.svg b/docs/chapter25/lattice.svg new file mode 100644 index 00000000..e72b473b --- /dev/null +++ b/docs/chapter25/lattice.svg @@ -0,0 +1,691 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 12.2.0 (20241103.1931) + --> +<!-- Title: Lattice Pages: 1 --> +<svg width="1330pt" height="584pt" + viewBox="0.00 0.00 1329.51 583.54" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 579.54)"> +<title>Lattice</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-579.54 1325.51,-579.54 1325.51,4 -4,4"/> +<!-- TOP --> +<g id="node1" class="node"> +<title>TOP</title> +<ellipse fill="black" stroke="black" cx="641.51" cy="-554.42" rx="21.12" ry="21.12"/> +<text text-anchor="start" x="633.63" y="-549.99" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text> +</g> +<!-- XCTRL --> +<g id="node3" class="node"> +<title>XCTRL</title> +<ellipse fill="yellow" stroke="black" cx="36.51" cy="-492.79" rx="36.51" ry="18"/> +<text text-anchor="start" x="17.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="33.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text> +</g> +<!-- TOP->XCTRL --> +<g id="edge1" class="edge"> +<title>TOP->XCTRL</title> +<path fill="none" stroke="black" d="M620.33,-551.33C533.82,-542.8 206.01,-510.5 83.28,-498.4"/> +<polygon fill="black" stroke="black" points="83.86,-494.94 73.56,-497.44 83.17,-501.91 83.86,-494.94"/> +</g> +<!-- INT_TOP --> +<g id="node5" class="node"> +<title>INT_TOP</title> +<ellipse fill="lightblue" stroke="black" cx="316.51" cy="-492.79" rx="33.95" ry="18"/> +<text text-anchor="start" x="299.63" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="315.38" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:int</text> +</g> +<!-- TOP->INT_TOP --> +<g id="edge4" class="edge"> +<title>TOP->INT_TOP</title> +<path fill="none" stroke="black" d="M620.68,-549.6C568.33,-539.99 430.36,-514.68 359.64,-501.71"/> +<polygon fill="black" stroke="black" points="360.44,-498.29 349.97,-499.93 359.18,-505.18 360.44,-498.29"/> +</g> +<!-- FLT_TOP --> +<g id="node14" class="node"> +<title>FLT_TOP</title> +<ellipse fill="aqua" stroke="black" cx="494.51" cy="-492.79" rx="32.41" ry="18"/> +<text text-anchor="start" x="478.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="494.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:flt</text> +</g> +<!-- TOP->FLT_TOP --> +<g id="edge18" class="edge"> +<title>TOP->FLT_TOP</title> +<path fill="none" stroke="black" d="M621.99,-545.5C598.79,-536.09 559.6,-520.19 530.78,-508.5"/> +<polygon fill="black" stroke="black" points="532.29,-505.34 521.7,-504.82 529.65,-511.82 532.29,-505.34"/> +</g> +<!-- PTR --> +<g id="node22" class="node"> +<title>PTR</title> +<polygon fill="none" stroke="none" points="668.51,-519.29 614.51,-519.29 614.51,-466.29 668.51,-466.29 668.51,-519.29"/> +<polygon fill="green" stroke="none" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/> +<polygon fill="none" stroke="black" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/> +<text text-anchor="start" x="630.63" y="-498.99" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr</text> +<polygon fill="lightgreen" stroke="none" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/> +<polygon fill="none" stroke="black" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/> +<text text-anchor="start" x="626.51" y="-476.49" font-family="Times New Roman,serif" font-size="14.00">struct</text> +</g> +<!-- TOP->PTR --> +<g id="edge33" class="edge"> +<title>TOP->PTR</title> +<path fill="none" stroke="black" d="M641.51,-532.9C641.51,-532.06 641.51,-531.22 641.51,-530.37"/> +<polygon fill="black" stroke="black" points="645.01,-530.61 641.51,-520.61 638.01,-530.61 645.01,-530.61"/> +</g> +<!-- STRUCT_TOP --> +<g id="node24" class="node"> +<title>STRUCT_TOP</title> +<ellipse fill="lightgreen" stroke="black" cx="746.51" cy="-492.79" rx="44.7" ry="18"/> +<text text-anchor="start" x="721.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="737.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:struct</text> +</g> +<!-- TOP->STRUCT_TOP --> +<g id="edge36" class="edge"> +<title>TOP->STRUCT_TOP</title> +<path fill="none" stroke="black" d="M659.8,-543.03C674.04,-534.94 694.24,-523.47 711.55,-513.64"/> +<polygon fill="black" stroke="black" points="713.07,-516.81 720.03,-508.83 709.61,-510.72 713.07,-516.81"/> +</g> +<!-- MEM_TOP --> +<g id="node29" class="node"> +<title>MEM_TOP</title> +<ellipse fill="blue" stroke="black" cx="998.51" cy="-492.79" rx="43.67" ry="18"/> +<text text-anchor="start" x="974.51" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text> +<text text-anchor="start" x="990.26" y="-488.74" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text> +</g> +<!-- TOP->MEM_TOP --> +<g id="edge46" class="edge"> +<title>TOP->MEM_TOP</title> +<path fill="none" stroke="black" d="M662.51,-549.91C717.64,-540.7 867.72,-515.64 947.33,-502.34"/> +<polygon fill="black" stroke="black" points="947.77,-505.82 957.06,-500.72 946.62,-498.91 947.77,-505.82"/> +</g> +<!-- TUPLE_TOP --> +<g id="node34" class="node"> +<title>TUPLE_TOP</title> +<ellipse fill="pink" stroke="black" cx="1182.51" cy="-492.79" rx="42.65" ry="18"/> +<text text-anchor="start" x="1159.26" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="1175.01" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:tuple</text> +</g> +<!-- TOP->TUPLE_TOP --> +<g id="edge56" class="edge"> +<title>TOP->TUPLE_TOP</title> +<path fill="none" stroke="black" d="M662.72,-551.08C740.91,-542.46 1014.51,-512.31 1130.21,-499.56"/> +<polygon fill="black" stroke="black" points="1130.5,-503.05 1140.05,-498.47 1129.73,-496.09 1130.5,-503.05"/> +</g> +<!-- BOT --> +<g id="node2" class="node"> +<title>BOT</title> +<ellipse fill="black" stroke="black" cx="641.51" cy="-21.12" rx="21.12" ry="21.12"/> +<text text-anchor="start" x="633.63" y="-16.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text> +</g> +<!-- CTRL --> +<g id="node4" class="node"> +<title>CTRL</title> +<ellipse fill="yellow" stroke="black" cx="49.51" cy="-82.75" rx="36.51" ry="18"/> +<text text-anchor="start" x="30.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="46.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text> +</g> +<!-- XCTRL->CTRL --> +<g id="edge2" class="edge"> +<title>XCTRL->CTRL</title> +<path fill="none" stroke="black" d="M37.09,-474.38C39.15,-409.6 46.05,-191.88 48.57,-112.25"/> +<polygon fill="black" stroke="black" points="52.06,-112.73 48.88,-102.63 45.06,-112.51 52.06,-112.73"/> +</g> +<!-- CTRL->BOT --> +<g id="edge3" class="edge"> +<title>CTRL->BOT</title> +<path fill="none" stroke="black" d="M85.21,-78.15C191.74,-67.42 506.36,-35.74 608.84,-25.41"/> +<polygon fill="black" stroke="black" points="609.15,-28.9 618.75,-24.42 608.45,-21.94 609.15,-28.9"/> +</g> +<!-- INT_HI --> +<g id="node7" class="node"> +<title>INT_HI</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-434.29" rx="53.91" ry="18"/> +<text text-anchor="middle" x="174.51" y="-429.24" font-family="Times New Roman,serif" font-size="14.00">[min>max]</text> +</g> +<!-- INT_TOP->INT_HI --> +<g id="edge5" class="edge"> +<title>INT_TOP->INT_HI</title> +<path fill="none" stroke="black" d="M289.8,-481.17C269.89,-473.25 242.26,-462.25 219.03,-453.01"/> +<polygon fill="black" stroke="black" points="220.37,-449.77 209.78,-449.33 217.78,-456.28 220.37,-449.77"/> +</g> +<!-- INT_BOT --> +<g id="node6" class="node"> +<title>INT_BOT</title> +<ellipse fill="lightblue" stroke="black" cx="329.51" cy="-82.75" rx="33.95" ry="18"/> +<text text-anchor="start" x="312.63" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="328.38" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:int</text> +</g> +<!-- INT_BOT->BOT --> +<g id="edge15" class="edge"> +<title>INT_BOT->BOT</title> +<path fill="none" stroke="black" d="M361.14,-75.7C420.84,-64.3 549.47,-39.71 609.47,-28.25"/> +<polygon fill="black" stroke="black" points="609.86,-31.73 619.03,-26.42 608.55,-24.86 609.86,-31.73"/> +</g> +<!-- INT_XBOOL --> +<g id="node8" class="node"> +<title>INT_XBOOL</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-373.97" rx="33.95" ry="18"/> +<text text-anchor="middle" x="97.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">[1...0]</text> +</g> +<!-- INT_HI->INT_XBOOL --> +<g id="edge6" class="edge"> +<title>INT_HI->INT_XBOOL</title> +<path fill="none" stroke="black" d="M153.47,-417.36C144.79,-410.78 134.6,-403.06 125.31,-396.03"/> +<polygon fill="black" stroke="black" points="127.71,-393.46 117.63,-390.21 123.48,-399.04 127.71,-393.46"/> +</g> +<!-- INT_CON --> +<g id="node9" class="node"> +<title>INT_CON</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-282.65" rx="45.21" ry="18"/> +<text text-anchor="middle" x="174.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">constants</text> +</g> +<!-- INT_HI->INT_CON --> +<g id="edge9" class="edge"> +<title>INT_HI->INT_CON</title> +<path fill="none" stroke="black" d="M174.51,-416C174.51,-390.27 174.51,-343.1 174.51,-312.31"/> +<polygon fill="black" stroke="black" points="178.01,-312.52 174.51,-302.52 171.01,-312.52 178.01,-312.52"/> +</g> +<!-- INT_CON0 --> +<g id="node10" class="node"> +<title>INT_CON0</title> +<ellipse fill="lightblue" stroke="black" cx="47.51" cy="-282.65" rx="18" ry="18"/> +<text text-anchor="middle" x="47.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0</text> +</g> +<!-- INT_XBOOL->INT_CON0 --> +<g id="edge7" class="edge"> +<title>INT_XBOOL->INT_CON0</title> +<path fill="none" stroke="black" d="M88.34,-356.59C80.82,-343.17 70.07,-323.96 61.49,-308.62"/> +<polygon fill="black" stroke="black" points="64.63,-307.08 56.69,-300.06 58.53,-310.5 64.63,-307.08"/> +</g> +<!-- INT_CON1 --> +<g id="node11" class="node"> +<title>INT_CON1</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-282.65" rx="18" ry="18"/> +<text text-anchor="middle" x="97.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">1</text> +</g> +<!-- INT_XBOOL->INT_CON1 --> +<g id="edge8" class="edge"> +<title>INT_XBOOL->INT_CON1</title> +<path fill="none" stroke="black" d="M97.51,-355.75C97.51,-343.47 97.51,-326.68 97.51,-312.39"/> +<polygon fill="black" stroke="black" points="101.01,-312.64 97.51,-302.64 94.01,-312.64 101.01,-312.64"/> +</g> +<!-- INT_LO --> +<g id="node13" class="node"> +<title>INT_LO</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-141.25" rx="53.91" ry="18"/> +<text text-anchor="middle" x="174.51" y="-136.2" font-family="Times New Roman,serif" font-size="14.00">[min<max]</text> +</g> +<!-- INT_CON->INT_LO --> +<g id="edge13" class="edge"> +<title>INT_CON->INT_LO</title> +<path fill="none" stroke="black" d="M174.51,-264.17C174.51,-240.45 174.51,-199.04 174.51,-170.9"/> +<polygon fill="black" stroke="black" points="178.01,-171.25 174.51,-161.25 171.01,-171.25 178.01,-171.25"/> +</g> +<!-- INT_CON0->INT_CON1 --> +<!-- INT_BOOL --> +<g id="node12" class="node"> +<title>INT_BOOL</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-196.45" rx="33.95" ry="18"/> +<text text-anchor="middle" x="97.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">[0...1]</text> +</g> +<!-- INT_CON0->INT_BOOL --> +<g id="edge10" class="edge"> +<title>INT_CON0->INT_BOOL</title> +<path fill="none" stroke="black" d="M56.45,-266.6C63.5,-254.72 73.51,-237.87 81.88,-223.77"/> +<polygon fill="black" stroke="black" points="84.75,-225.79 86.85,-215.41 78.73,-222.22 84.75,-225.79"/> +</g> +<!-- INT_CON1->INT_CON --> +<!-- INT_CON1->INT_BOOL --> +<g id="edge11" class="edge"> +<title>INT_CON1->INT_BOOL</title> +<path fill="none" stroke="black" d="M97.51,-264.21C97.51,-253.25 97.51,-238.87 97.51,-226.25"/> +<polygon fill="black" stroke="black" points="101.01,-226.39 97.51,-216.39 94.01,-226.39 101.01,-226.39"/> +</g> +<!-- INT_BOOL->INT_LO --> +<g id="edge12" class="edge"> +<title>INT_BOOL->INT_LO</title> +<path fill="none" stroke="black" d="M117.34,-181.75C124.98,-176.47 133.91,-170.3 142.42,-164.42"/> +<polygon fill="black" stroke="black" points="144.27,-167.4 150.5,-158.83 140.29,-161.64 144.27,-167.4"/> +</g> +<!-- INT_LO->INT_BOT --> +<g id="edge14" class="edge"> +<title>INT_LO->INT_BOT</title> +<path fill="none" stroke="black" d="M210.07,-127.29C234.52,-118.37 266.93,-106.56 291.82,-97.49"/> +<polygon fill="black" stroke="black" points="292.71,-100.89 300.91,-94.17 290.31,-94.31 292.71,-100.89"/> +</g> +<!-- FLT_CON64 --> +<g id="node16" class="node"> +<title>FLT_CON64</title> +<ellipse fill="aqua" stroke="black" cx="564.51" cy="-282.65" rx="55.33" ry="40.66"/> +<text text-anchor="middle" x="564.51" y="-294.1" font-family="Times New Roman,serif" font-size="14.00">64b consts</text> +<text text-anchor="middle" x="564.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">[excludes</text> +<text text-anchor="middle" x="564.51" y="-261.1" font-family="Times New Roman,serif" font-size="14.00">32b consts]</text> +</g> +<!-- FLT_TOP->FLT_CON64 --> +<g id="edge19" class="edge"> +<title>FLT_TOP->FLT_CON64</title> +<path fill="none" stroke="black" d="M500.48,-474.88C510.8,-443.88 532.35,-379.2 547.59,-333.44"/> +<polygon fill="black" stroke="black" points="550.9,-334.57 550.74,-323.98 544.26,-332.36 550.9,-334.57"/> +</g> +<!-- FLT_CONPI --> +<g id="node18" class="node"> +<title>FLT_CONPI</title> +<ellipse fill="aqua" stroke="black" cx="446.51" cy="-282.65" rx="48.79" ry="18"/> +<text text-anchor="middle" x="446.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">3.14159...</text> +</g> +<!-- FLT_TOP->FLT_CONPI --> +<g id="edge20" class="edge"> +<title>FLT_TOP->FLT_CONPI</title> +<path fill="none" stroke="black" d="M490.33,-474.51C482.02,-438.11 463.32,-356.27 453.25,-312.16"/> +<polygon fill="black" stroke="black" points="456.69,-311.49 451.05,-302.52 449.86,-313.04 456.69,-311.49"/> +</g> +<!-- FLT_X32 --> +<g id="node19" class="node"> +<title>FLT_X32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-373.97" rx="28.32" ry="28.32"/> +<text text-anchor="middle" x="333.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">~f32</text> +</g> +<!-- FLT_TOP->FLT_X32 --> +<g id="edge23" class="edge"> +<title>FLT_TOP->FLT_X32</title> +<path fill="none" stroke="black" d="M474.77,-478.22C447.56,-458.14 398.22,-421.73 365.65,-397.69"/> +<polygon fill="black" stroke="black" points="367.88,-394.99 357.75,-391.86 363.72,-400.62 367.88,-394.99"/> +</g> +<!-- FLT_BOT --> +<g id="node15" class="node"> +<title>FLT_BOT</title> +<ellipse fill="aqua" stroke="black" cx="507.51" cy="-82.75" rx="32.41" ry="18"/> +<text text-anchor="start" x="491.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="507.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:flt</text> +</g> +<!-- FLT_BOT->BOT --> +<g id="edge29" class="edge"> +<title>FLT_BOT->BOT</title> +<path fill="none" stroke="black" d="M532.4,-70.67C554.98,-60.63 588.29,-45.81 612.16,-35.19"/> +<polygon fill="black" stroke="black" points="613.29,-38.51 621,-31.25 610.44,-32.12 613.29,-38.51"/> +</g> +<!-- FLT_CON64->FLT_BOT --> +<g id="edge21" class="edge"> +<title>FLT_CON64->FLT_BOT</title> +<path fill="none" stroke="black" d="M553.11,-242.68C542.05,-203.88 525.53,-145.95 515.71,-111.5"/> +<polygon fill="black" stroke="black" points="519.17,-110.89 513.06,-102.23 512.44,-112.81 519.17,-110.89"/> +</g> +<!-- FLT_CON32 --> +<g id="node17" class="node"> +<title>FLT_CON32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-282.65" rx="50.33" ry="18"/> +<text text-anchor="middle" x="333.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">32b consts</text> +</g> +<!-- FLT_CON32->FLT_CONPI --> +<!-- FLT_32 --> +<g id="node21" class="node"> +<title>FLT_32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-196.45" rx="23.2" ry="23.2"/> +<text text-anchor="middle" x="333.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">f32</text> +</g> +<!-- FLT_CON32->FLT_32 --> +<g id="edge27" class="edge"> +<title>FLT_CON32->FLT_32</title> +<path fill="none" stroke="black" d="M333.51,-264.21C333.51,-254.7 333.51,-242.59 333.51,-231.31"/> +<polygon fill="black" stroke="black" points="337.01,-231.51 333.51,-221.51 330.01,-231.51 337.01,-231.51"/> +</g> +<!-- FLT_CONPI->FLT_BOT --> +<g id="edge22" class="edge"> +<title>FLT_CONPI->FLT_BOT</title> +<path fill="none" stroke="black" d="M452.03,-264.56C462.62,-229.86 485.86,-153.69 498.65,-111.79"/> +<polygon fill="black" stroke="black" points="501.94,-112.98 501.51,-102.39 495.25,-110.93 501.94,-112.98"/> +</g> +<!-- FLT_CONPI->FLT_CON64 --> +<!-- FLT_X32->FLT_CON32 --> +<g id="edge25" class="edge"> +<title>FLT_X32->FLT_CON32</title> +<path fill="none" stroke="black" d="M333.51,-345.2C333.51,-334.71 333.51,-322.76 333.51,-312.19"/> +<polygon fill="black" stroke="black" points="337.01,-312.52 333.51,-302.52 330.01,-312.53 337.01,-312.52"/> +</g> +<!-- FLT_CON0 --> +<g id="node20" class="node"> +<title>FLT_CON0</title> +<ellipse fill="aqua" stroke="black" cx="251.51" cy="-282.65" rx="18.08" ry="18.08"/> +<text text-anchor="middle" x="251.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0.</text> +</g> +<!-- FLT_X32->FLT_CON0 --> +<g id="edge24" class="edge"> +<title>FLT_X32->FLT_CON0</title> +<path fill="none" stroke="black" d="M314.9,-352.7C301.83,-338.47 284.38,-319.46 271.1,-304.99"/> +<polygon fill="black" stroke="black" points="273.81,-302.77 264.47,-297.77 268.65,-307.5 273.81,-302.77"/> +</g> +<!-- FLT_CON0->FLT_CON32 --> +<!-- FLT_CON0->FLT_32 --> +<g id="edge26" class="edge"> +<title>FLT_CON0->FLT_32</title> +<path fill="none" stroke="black" d="M263.96,-268.86C276.06,-256.44 294.74,-237.26 309.69,-221.91"/> +<polygon fill="black" stroke="black" points="312.03,-224.53 316.5,-214.92 307.01,-219.64 312.03,-224.53"/> +</g> +<!-- FLT_32->FLT_BOT --> +<g id="edge28" class="edge"> +<title>FLT_32->FLT_BOT</title> +<path fill="none" stroke="black" d="M353.13,-183.63C383.19,-163.99 440.9,-126.28 476.61,-102.94"/> +<polygon fill="black" stroke="black" points="478.34,-105.99 484.79,-97.59 474.51,-100.13 478.34,-105.99"/> +</g> +<!-- PTR0 --> +<g id="node23" class="node"> +<title>PTR0</title> +<polygon fill="none" stroke="none" points="668.51,-109.25 614.51,-109.25 614.51,-56.25 668.51,-56.25 668.51,-109.25"/> +<polygon fill="green" stroke="none" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/> +<polygon fill="none" stroke="black" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/> +<text text-anchor="start" x="627.63" y="-88.95" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr?</text> +<polygon fill="lightgreen" stroke="none" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/> +<polygon fill="none" stroke="black" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/> +<text text-anchor="start" x="626.51" y="-66.45" font-family="Times New Roman,serif" font-size="14.00">struct</text> +</g> +<!-- PTR->PTR0 --> +<g id="edge34" class="edge"> +<title>PTR->PTR0</title> +<path fill="none" stroke="black" d="M641.51,-466.52C641.51,-396.01 641.51,-202.44 641.51,-120.34"/> +<polygon fill="black" stroke="black" points="645.01,-120.58 641.51,-110.58 638.01,-120.58 645.01,-120.58"/> +</g> +<!-- PTR0->BOT --> +<g id="edge35" class="edge"> +<title>PTR0->BOT</title> +<path fill="none" stroke="black" d="M641.51,-56.46C641.51,-55.58 641.51,-54.7 641.51,-53.81"/> +<polygon fill="black" stroke="black" points="645.01,-53.9 641.51,-43.9 638.01,-53.9 645.01,-53.9"/> +</g> +<!-- STRUCT_S1 --> +<g id="node26" class="node"> +<title>STRUCT_S1</title> +<polygon fill="none" stroke="none" points="741.38,-300.65 663.63,-300.65 663.63,-264.65 741.38,-264.65 741.38,-300.65"/> +<polygon fill="lightgreen" stroke="none" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/> +<polygon fill="none" stroke="black" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/> +<text text-anchor="start" x="672.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_0" gradientUnits="userSpaceOnUse" x1="703.51" y1="-289.19" x2="728.26" y2="-276.11" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_0)" stroke="none" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/> +<polygon fill="none" stroke="black" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/> +<text text-anchor="start" x="699.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_S1 --> +<g id="edge37" class="edge"> +<title>STRUCT_TOP->STRUCT_S1</title> +<path fill="none" stroke="black" d="M742.68,-474.51C735.02,-437.92 717.75,-355.45 708.55,-311.5"/> +<polygon fill="black" stroke="black" points="712.02,-311 706.54,-301.93 705.17,-312.43 712.02,-311"/> +</g> +<!-- STRUCT_S2 --> +<g id="node27" class="node"> +<title>STRUCT_S2</title> +<polygon fill="none" stroke="none" points="841.38,-309.15 755.63,-309.15 755.63,-256.15 841.38,-256.15 841.38,-309.15"/> +<polygon fill="lightgreen" stroke="none" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/> +<polygon fill="none" stroke="black" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/> +<text text-anchor="start" x="766.63" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_1" gradientUnits="userSpaceOnUse" x1="800.1" y1="-301.86" x2="827.67" y2="-285.95" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_1)" stroke="none" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/> +<polygon fill="none" stroke="black" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/> +<text text-anchor="start" x="797.38" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/> +<polygon fill="none" stroke="black" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/> +<text text-anchor="start" x="766.63" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_2" gradientUnits="userSpaceOnUse" x1="800.1" y1="-279.36" x2="827.67" y2="-263.45" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_2)" stroke="none" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/> +<polygon fill="none" stroke="black" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/> +<text text-anchor="start" x="797.38" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_S2 --> +<g id="edge38" class="edge"> +<title>STRUCT_TOP->STRUCT_S2</title> +<path fill="none" stroke="black" d="M751.03,-474.51C759.53,-440.16 778.05,-365.34 789.28,-319.95"/> +<polygon fill="black" stroke="black" points="792.67,-320.83 791.67,-310.28 785.87,-319.15 792.67,-320.83"/> +</g> +<!-- STRUCT_SN --> +<g id="node28" class="node"> +<title>STRUCT_SN</title> +<polygon fill="none" stroke="none" points="941.38,-320.4 855.63,-320.4 855.63,-244.9 941.38,-244.9 941.38,-320.4"/> +<polygon fill="lightgreen" stroke="none" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/> +<polygon fill="none" stroke="black" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/> +<text text-anchor="start" x="866.63" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_3" gradientUnits="userSpaceOnUse" x1="900.1" y1="-313.11" x2="927.67" y2="-297.2" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_3)" stroke="none" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/> +<polygon fill="none" stroke="black" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/> +<text text-anchor="start" x="897.38" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/> +<polygon fill="none" stroke="black" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/> +<text text-anchor="start" x="869.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">...  </text> +<defs> +<linearGradient id="l_4" gradientUnits="userSpaceOnUse" x1="900.1" y1="-290.61" x2="927.67" y2="-274.7" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_4)" stroke="none" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/> +<polygon fill="none" stroke="black" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/> +<text text-anchor="start" x="897.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/> +<polygon fill="none" stroke="black" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/> +<text text-anchor="start" x="866.63" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_5" gradientUnits="userSpaceOnUse" x1="900.1" y1="-268.11" x2="927.67" y2="-252.2" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_5)" stroke="none" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/> +<polygon fill="none" stroke="black" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/> +<text text-anchor="start" x="897.38" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_SN --> +<g id="edge39" class="edge"> +<title>STRUCT_TOP->STRUCT_SN</title> +<path fill="none" stroke="black" d="M759.21,-475.24C782.17,-443.49 831.3,-375.56 864.63,-329.48"/> +<polygon fill="black" stroke="black" points="867.37,-331.67 870.4,-321.52 861.7,-327.57 867.37,-331.67"/> +</g> +<!-- STRUCT_BOT --> +<g id="node25" class="node"> +<title>STRUCT_BOT</title> +<ellipse fill="lightgreen" stroke="black" cx="751.51" cy="-82.75" rx="44.7" ry="18"/> +<text text-anchor="start" x="726.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="742.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:struct</text> +</g> +<!-- STRUCT_BOT->BOT --> +<g id="edge43" class="edge"> +<title>STRUCT_BOT->BOT</title> +<path fill="none" stroke="black" d="M725.72,-67.77C709,-58.7 687.2,-46.89 669.97,-37.55"/> +<polygon fill="black" stroke="black" points="671.71,-34.51 661.25,-32.82 668.37,-40.67 671.71,-34.51"/> +</g> +<!-- STRUCT_S1->STRUCT_BOT --> +<g id="edge40" class="edge"> +<title>STRUCT_S1->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M706.86,-264.91C715.3,-230.49 733.97,-154.31 744.29,-112.19"/> +<polygon fill="black" stroke="black" points="747.69,-113.04 746.67,-102.5 740.89,-111.37 747.69,-113.04"/> +</g> +<!-- STRUCT_S1->STRUCT_S2 --> +<!-- STRUCT_S2->STRUCT_BOT --> +<g id="edge41" class="edge"> +<title>STRUCT_S2->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M792.36,-256.52C783.62,-219.3 767.61,-151.25 758.45,-112.26"/> +<polygon fill="black" stroke="black" points="761.88,-111.57 756.18,-102.64 755.07,-113.17 761.88,-111.57"/> +</g> +<!-- STRUCT_S2->STRUCT_SN --> +<!-- STRUCT_SN->STRUCT_BOT --> +<g id="edge42" class="edge"> +<title>STRUCT_SN->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M870.86,-245.05C841.74,-205.46 796.64,-144.13 771.03,-109.3"/> +<polygon fill="black" stroke="black" points="773.99,-107.42 765.25,-101.44 768.36,-111.57 773.99,-107.42"/> +</g> +<!-- MEM_ALIAS1 --> +<g id="node31" class="node"> +<title>MEM_ALIAS1</title> +<ellipse fill="blue" stroke="black" cx="975.51" cy="-282.65" rx="20.13" ry="20.13"/> +<text text-anchor="middle" x="975.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#1</text> +</g> +<!-- MEM_TOP->MEM_ALIAS1 --> +<g id="edge47" class="edge"> +<title>MEM_TOP->MEM_ALIAS1</title> +<path fill="none" stroke="black" d="M996.51,-474.51C992.58,-438.66 983.84,-358.73 978.96,-314.18"/> +<polygon fill="black" stroke="black" points="982.45,-313.94 977.89,-304.38 975.5,-314.7 982.45,-313.94"/> +</g> +<!-- MEM_ALIASDOTS --> +<g id="node32" class="node"> +<title>MEM_ALIASDOTS</title> +<ellipse fill="blue" stroke="black" cx="1036.51" cy="-282.65" rx="27" ry="18"/> +<text text-anchor="middle" x="1036.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text> +</g> +<!-- MEM_TOP->MEM_ALIASDOTS --> +<g id="edge48" class="edge"> +<title>MEM_TOP->MEM_ALIASDOTS</title> +<path fill="none" stroke="black" d="M1001.81,-474.51C1008.41,-438.02 1023.27,-355.86 1031.23,-311.83"/> +<polygon fill="black" stroke="black" points="1034.63,-312.69 1032.97,-302.23 1027.74,-311.45 1034.63,-312.69"/> +</g> +<!-- MEM_ALIASN --> +<g id="node33" class="node"> +<title>MEM_ALIASN</title> +<ellipse fill="blue" stroke="black" cx="1097.51" cy="-282.65" rx="20.13" ry="20.13"/> +<text text-anchor="middle" x="1097.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#n</text> +</g> +<!-- MEM_TOP->MEM_ALIASN --> +<g id="edge49" class="edge"> +<title>MEM_TOP->MEM_ALIASN</title> +<path fill="none" stroke="black" d="M1006.95,-474.88C1024.11,-438.46 1063.18,-355.52 1083.95,-311.44"/> +<polygon fill="black" stroke="black" points="1087.07,-313.03 1088.16,-302.49 1080.73,-310.05 1087.07,-313.03"/> +</g> +<!-- MEM_BOT --> +<g id="node30" class="node"> +<title>MEM_BOT</title> +<ellipse fill="blue" stroke="black" cx="998.51" cy="-82.75" rx="43.67" ry="18"/> +<text text-anchor="start" x="974.51" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text> +<text text-anchor="start" x="990.26" y="-78.7" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text> +</g> +<!-- MEM_BOT->BOT --> +<g id="edge53" class="edge"> +<title>MEM_BOT->BOT</title> +<path fill="none" stroke="black" d="M958.6,-75.08C887.17,-63.15 738.86,-38.38 673.56,-27.48"/> +<polygon fill="black" stroke="black" points="674.3,-24.05 663.86,-25.86 673.15,-30.96 674.3,-24.05"/> +</g> +<!-- MEM_ALIAS1->MEM_BOT --> +<g id="edge50" class="edge"> +<title>MEM_ALIAS1->MEM_BOT</title> +<path fill="none" stroke="black" d="M977.84,-262.38C981.92,-226.96 990.35,-153.62 995.09,-112.45"/> +<polygon fill="black" stroke="black" points="998.57,-112.87 996.23,-102.53 991.61,-112.07 998.57,-112.87"/> +</g> +<!-- MEM_ALIAS1->MEM_ALIASDOTS --> +<!-- MEM_ALIASDOTS->MEM_BOT --> +<g id="edge51" class="edge"> +<title>MEM_ALIASDOTS->MEM_BOT</title> +<path fill="none" stroke="black" d="M1033.07,-264.56C1026.49,-229.93 1012.06,-154.01 1004.08,-112.06"/> +<polygon fill="black" stroke="black" points="1007.56,-111.6 1002.25,-102.43 1000.68,-112.91 1007.56,-111.6"/> +</g> +<!-- MEM_ALIASDOTS->MEM_ALIASN --> +<!-- MEM_ALIASN->MEM_BOT --> +<g id="edge52" class="edge"> +<title>MEM_ALIASN->MEM_BOT</title> +<path fill="none" stroke="black" d="M1088.37,-264.2C1070.97,-229.07 1032.98,-152.35 1012.41,-110.82"/> +<polygon fill="black" stroke="black" points="1015.58,-109.34 1008.01,-101.93 1009.31,-112.45 1015.58,-109.34"/> +</g> +<!-- TUPLE1 --> +<g id="node36" class="node"> +<title>TUPLE1</title> +<polygon fill="none" stroke="none" points="1185.51,-309.15 1131.51,-309.15 1131.51,-256.15 1185.51,-256.15 1185.51,-309.15"/> +<polygon fill="pink" stroke="none" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/> +<polygon fill="none" stroke="black" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/> +<text text-anchor="start" x="1142.76" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/> +<polygon fill="none" stroke="black" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/> +<text text-anchor="start" x="1146.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLE1 --> +<g id="edge57" class="edge"> +<title>TUPLE_TOP->TUPLE1</title> +<path fill="none" stroke="black" d="M1180.42,-474.51C1176.51,-440.23 1167.99,-365.66 1162.8,-320.24"/> +<polygon fill="black" stroke="black" points="1166.28,-319.86 1161.67,-310.32 1159.33,-320.65 1166.28,-319.86"/> +</g> +<!-- TUPLE2 --> +<g id="node37" class="node"> +<title>TUPLE2</title> +<polygon fill="none" stroke="none" points="1253.51,-320.4 1199.51,-320.4 1199.51,-244.9 1253.51,-244.9 1253.51,-320.4"/> +<polygon fill="pink" stroke="none" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/> +<polygon fill="none" stroke="black" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/> +<text text-anchor="start" x="1210.76" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/> +<polygon fill="none" stroke="black" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/> +<text text-anchor="start" x="1214.88" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +<polygon fill="black" stroke="none" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/> +<polygon fill="none" stroke="black" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/> +<text text-anchor="start" x="1214.88" y="-255.1" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLE2 --> +<g id="edge58" class="edge"> +<title>TUPLE_TOP->TUPLE2</title> +<path fill="none" stroke="black" d="M1186.34,-474.51C1192.95,-442.94 1206.72,-377.19 1216.28,-331.51"/> +<polygon fill="black" stroke="black" points="1219.68,-332.35 1218.3,-321.85 1212.83,-330.92 1219.68,-332.35"/> +</g> +<!-- TUPLEN --> +<g id="node38" class="node"> +<title>TUPLEN</title> +<polygon fill="none" stroke="none" points="1321.51,-331.65 1267.51,-331.65 1267.51,-233.65 1321.51,-233.65 1321.51,-331.65"/> +<polygon fill="pink" stroke="none" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/> +<polygon fill="none" stroke="black" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/> +<text text-anchor="start" x="1278.76" y="-311.35" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/> +<polygon fill="none" stroke="black" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/> +<text text-anchor="start" x="1282.88" y="-288.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +<polygon fill="black" stroke="none" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/> +<polygon fill="none" stroke="black" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/> +<text text-anchor="start" x="1288.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text> +<polygon fill="black" stroke="none" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/> +<polygon fill="none" stroke="black" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/> +<text text-anchor="start" x="1282.88" y="-243.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLEN --> +<g id="edge59" class="edge"> +<title>TUPLE_TOP->TUPLEN</title> +<path fill="none" stroke="black" d="M1192.06,-474.88C1207.65,-445.62 1239.24,-386.36 1263.24,-341.32"/> +<polygon fill="black" stroke="black" points="1266.22,-343.18 1267.83,-332.71 1260.04,-339.88 1266.22,-343.18"/> +</g> +<!-- TUPLE_BOT --> +<g id="node35" class="node"> +<title>TUPLE_BOT</title> +<ellipse fill="pink" stroke="black" cx="1192.51" cy="-82.75" rx="42.65" ry="18"/> +<text text-anchor="start" x="1169.26" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="1185.01" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:tuple</text> +</g> +<!-- TUPLE_BOT->BOT --> +<g id="edge63" class="edge"> +<title>TUPLE_BOT->BOT</title> +<path fill="none" stroke="black" d="M1151.54,-77.32C1046.53,-65.95 769.32,-35.95 674.14,-25.65"/> +<polygon fill="black" stroke="black" points="674.52,-22.18 664.2,-24.58 673.77,-29.14 674.52,-22.18"/> +</g> +<!-- TUPLE1->TUPLE_BOT --> +<g id="edge60" class="edge"> +<title>TUPLE1->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1162.95,-256.52C1169.28,-219.3 1180.86,-151.25 1187.49,-112.26"/> +<polygon fill="black" stroke="black" points="1190.9,-113.1 1189.12,-102.66 1184,-111.93 1190.9,-113.1"/> +</g> +<!-- TUPLE2->TUPLE_BOT --> +<g id="edge61" class="edge"> +<title>TUPLE2->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1220.11,-245.05C1213.55,-206.44 1203.46,-147.16 1197.47,-111.94"/> +<polygon fill="black" stroke="black" points="1200.99,-111.74 1195.86,-102.47 1194.09,-112.92 1200.99,-111.74"/> +</g> +<!-- TUPLEN->TUPLE_BOT --> +<g id="edge62" class="edge"> +<title>TUPLEN->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1269.56,-233.75C1249.85,-195.12 1223.04,-142.58 1206.8,-110.75"/> +<polygon fill="black" stroke="black" points="1210.07,-109.46 1202.41,-102.15 1203.83,-112.65 1210.07,-109.46"/> +</g> +</g> +</svg> diff --git a/docs/chapter25/lattice2.gv b/docs/chapter25/lattice2.gv new file mode 100644 index 00000000..6aa37fa0 --- /dev/null +++ b/docs/chapter25/lattice2.gv @@ -0,0 +1,85 @@ +digraph Lattice { + compound = true; + // Define nodes + splines=false; + node [shape=oval, style=filled]; + nodesep=0.2; + ranksep=0.4; + + TOP [label="~scalar\n+int+tmp+0", fillcolor=black, fontcolor=white, group=center]; + + // ------------------------ + PTRHQ [label="~tmp\n+tmp+0", fillcolor=green, fontcolor=white, group=left]; + TOP -> PTRHQ + + VALH_ [label="~nscalar\n+int+tmp", fillcolor=white, group=center]; + TOP -> VALH_ + + INTHQ [label="~int\n+int+0", fillcolor=lightblue, group=right]; + TOP -> INTHQ + + LEFTHI [label="junk", style=invis, group=junkleft] + + + PTRHQ -> LEFTHI-> VALH_ -> INTHQ [style=invis]; + { rank=same; PTRHQ; LEFTHI; VALH_; INTHQ } + + // ------------------------ + PTRH_ [label="~ntmp\n+tmp", fillcolor=green, fontcolor=white, group=left]; + VALH_ -> PTRH_ + PTRHQ -> PTRH_ + + INTH_ [label="~nint\n+int", fillcolor=lightblue, group=right]; + VALH_ -> INTH_ + INTHQ -> INTH_ + { rank=same; PTRH_; INTH_ } + + // ------------------------ + NILX_ [label="~nil\n[int,tmp]+0", fillcolor=aqua, group=junkleft]; + INTHQ -> NILX_ + PTRHQ -> NILX_ + + NIL__ [label="nil\n[+int+tmp],0", fillcolor=pink]; + VALH_ -> NIL__; + + NILX_ -> NIL__ [style=invis]; + { rank=same; NIL__; NILX_ } + + // ------------------------ + PTRL_ [label="ntmp\ntmp", fillcolor=green, fontcolor=white, group=left]; + PTRH_ -> PTRL_ + + INTL_ [label="nint\nint", fillcolor=lightblue, group=right]; + INTH_ -> INTL_ + { rank=same; PTRL_; INTL_ } + + // ------------------------ + PTRL0 [label="tmp\ntmp,0", fillcolor=green, fontcolor=white, group=left]; + PTRL_ -> PTRL0 + NIL__ -> PTRL0 + + VALL_ [label="nscalar\nint,tmp", fillcolor=white, group=center]; + PTRL_ -> VALL_ + INTL_ -> VALL_ + NILX_ -> VALL_ + + INTL0 [label="int\nint,0", fillcolor=lightblue, group=right]; + INTL_ -> INTL0 + NIL__ -> INTL0 + + PTRL0 -> VALL_ -> INTL0 [style=invis]; + { rank=same; PTRL0; VALL_; INTL0 } + + // ------------------------ + BOT [label="scalar\nint,tmp,0", fillcolor=black, fontcolor=white, group=center]; + PTRL0 -> BOT + INTL0 -> BOT + VALL_ -> BOT + + // ------------------------ + // Verticle alignment + TOP -> VALH_ -> VALL_ -> BOT [style=invis]; + PTRHQ -> PTRH_ -> PTRL_ -> PTRL0 [style=invis]; + INTHQ -> INTH_ -> INTL_ -> INTL0 [style=invis]; + LEFTHI -> NILX_ [style=invis] +} diff --git a/docs/chapter25/lattice2.svg b/docs/chapter25/lattice2.svg new file mode 100644 index 00000000..8b682aa7 --- /dev/null +++ b/docs/chapter25/lattice2.svg @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 12.2.0 (20241103.1931) + --> +<!-- Title: Lattice Pages: 1 --> +<svg width="478pt" height="501pt" + viewBox="0.00 0.00 477.96 500.90" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 496.9)"> +<title>Lattice</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-496.9 473.96,-496.9 473.96,4 -4,4"/> +<!-- TOP --> +<g id="node1" class="node"> +<title>TOP</title> +<ellipse fill="black" stroke="black" cx="240.13" cy="-463.91" rx="59.04" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-467.11" font-family="Times New Roman,serif" font-size="14.00" fill="white">~scalar</text> +<text text-anchor="middle" x="240.13" y="-450.61" font-family="Times New Roman,serif" font-size="14.00" fill="white">+int+tmp+0</text> +</g> +<!-- PTRHQ --> +<g id="node2" class="node"> +<title>PTRHQ</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-376.92" rx="43.13" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00" fill="white">~tmp</text> +<text text-anchor="middle" x="43.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp+0</text> +</g> +<!-- TOP->PTRHQ --> +<g id="edge1" class="edge"> +<title>TOP->PTRHQ</title> +<path fill="none" stroke="black" d="M196.41,-444.04C164.74,-430.38 121.99,-411.94 89.68,-398"/> +<polygon fill="black" stroke="black" points="91.11,-394.81 80.55,-394.06 88.34,-401.24 91.11,-394.81"/> +</g> +<!-- VALH_ --> +<g id="node3" class="node"> +<title>VALH_</title> +<ellipse fill="white" stroke="black" cx="240.13" cy="-376.92" rx="48.44" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~nscalar</text> +<text text-anchor="middle" x="240.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+tmp</text> +</g> +<!-- TOP->VALH_ --> +<g id="edge2" class="edge"> +<title>TOP->VALH_</title> +<path fill="none" stroke="black" d="M235.15,-434.61C234.93,-429.02 234.85,-423.06 234.9,-417.18"/> +<polygon fill="black" stroke="black" points="238.39,-417.69 235.12,-407.61 231.39,-417.52 238.39,-417.69"/> +</g> +<!-- TOP->VALH_ --> +<!-- INTHQ --> +<g id="node4" class="node"> +<title>INTHQ</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-376.92" rx="37.83" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~int</text> +<text text-anchor="middle" x="432.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+0</text> +</g> +<!-- TOP->INTHQ --> +<g id="edge3" class="edge"> +<title>TOP->INTHQ</title> +<path fill="none" stroke="black" d="M283.22,-443.83C314.91,-429.81 357.78,-410.83 389.39,-396.84"/> +<polygon fill="black" stroke="black" points="390.53,-400.16 398.26,-392.92 387.7,-393.76 390.53,-400.16"/> +</g> +<!-- LEFTHI --> +<!-- PTRHQ->LEFTHI --> +<!-- PTRH_ --> +<g id="node6" class="node"> +<title>PTRH_</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-289.94" rx="36.77" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00" fill="white">~ntmp</text> +<text text-anchor="middle" x="43.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp</text> +</g> +<!-- PTRHQ->PTRH_ --> +<g id="edge8" class="edge"> +<title>PTRHQ->PTRH_</title> +<path fill="none" stroke="black" d="M38.15,-347.62C37.93,-342.03 37.85,-336.07 37.9,-330.2"/> +<polygon fill="black" stroke="black" points="41.39,-330.71 38.12,-320.63 34.39,-330.54 41.39,-330.71"/> +</g> +<!-- PTRHQ->PTRH_ --> +<!-- NILX_ --> +<g id="node8" class="node"> +<title>NILX_</title> +<ellipse fill="aqua" stroke="black" cx="150.13" cy="-289.94" rx="56.39" ry="28.99"/> +<text text-anchor="middle" x="150.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nil</text> +<text text-anchor="middle" x="150.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[int,tmp]+0</text> +</g> +<!-- PTRHQ->NILX_ --> +<g id="edge12" class="edge"> +<title>PTRHQ->NILX_</title> +<path fill="none" stroke="black" d="M70.41,-354.26C82.91,-344.33 97.94,-332.4 111.56,-321.58"/> +<polygon fill="black" stroke="black" points="113.43,-324.56 119.09,-315.6 109.08,-319.08 113.43,-324.56"/> +</g> +<!-- VALH_->INTHQ --> +<!-- VALH_->PTRH_ --> +<g id="edge7" class="edge"> +<title>VALH_->PTRH_</title> +<path fill="none" stroke="black" d="M201.21,-359.13C167.78,-344.71 119.68,-323.96 85.33,-309.14"/> +<polygon fill="black" stroke="black" points="87.1,-306.09 76.53,-305.35 84.33,-312.52 87.1,-306.09"/> +</g> +<!-- INTH_ --> +<g id="node7" class="node"> +<title>INTH_</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-289.94" rx="31.47" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nint</text> +<text text-anchor="middle" x="432.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">+int</text> +</g> +<!-- VALH_->INTH_ --> +<g id="edge9" class="edge"> +<title>VALH_->INTH_</title> +<path fill="none" stroke="black" d="M278.53,-358.93C311.9,-344.16 360.02,-322.86 393.48,-308.05"/> +<polygon fill="black" stroke="black" points="394.67,-311.35 402.4,-304.1 391.84,-304.95 394.67,-311.35"/> +</g> +<!-- NIL__ --> +<g id="node9" class="node"> +<title>NIL__</title> +<ellipse fill="pink" stroke="black" cx="324.13" cy="-289.94" rx="62.23" ry="28.99"/> +<text text-anchor="middle" x="324.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">nil</text> +<text text-anchor="middle" x="324.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[+int+tmp],0</text> +</g> +<!-- VALH_->NIL__ --> +<g id="edge13" class="edge"> +<title>VALH_->NIL__</title> +<path fill="none" stroke="black" d="M264.43,-351.34C272.67,-343.01 282.01,-333.55 290.83,-324.64"/> +<polygon fill="black" stroke="black" points="293.12,-327.3 297.66,-317.72 288.14,-322.38 293.12,-327.3"/> +</g> +<!-- VALL_ --> +<g id="node13" class="node"> +<title>VALL_</title> +<ellipse fill="white" stroke="black" cx="240.13" cy="-115.97" rx="39.42" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">nscalar</text> +<text text-anchor="middle" x="240.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,tmp</text> +</g> +<!-- VALH_->VALL_ --> +<!-- INTHQ->INTH_ --> +<g id="edge10" class="edge"> +<title>INTHQ->INTH_</title> +<path fill="none" stroke="black" d="M427.17,-348.1C426.94,-342.37 426.85,-336.25 426.9,-330.21"/> +<polygon fill="black" stroke="black" points="430.39,-330.42 427.13,-320.34 423.39,-330.26 430.39,-330.42"/> +</g> +<!-- INTHQ->INTH_ --> +<!-- INTHQ->NILX_ --> +<g id="edge11" class="edge"> +<title>INTHQ->NILX_</title> +<path fill="none" stroke="black" d="M396.99,-365.33C350,-351.17 266.03,-325.87 209.23,-308.75"/> +<polygon fill="black" stroke="black" points="210.28,-305.41 199.69,-305.88 208.26,-312.11 210.28,-305.41"/> +</g> +<!-- LEFTHI->VALH_ --> +<!-- LEFTHI->NILX_ --> +<!-- PTRL_ --> +<g id="node10" class="node"> +<title>PTRL_</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-202.96" rx="31.47" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00" fill="white">ntmp</text> +<text text-anchor="middle" x="43.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text> +</g> +<!-- PTRH_->PTRL_ --> +<g id="edge15" class="edge"> +<title>PTRH_->PTRL_</title> +<path fill="none" stroke="black" d="M38.17,-261.11C37.94,-255.39 37.85,-249.26 37.9,-243.22"/> +<polygon fill="black" stroke="black" points="41.39,-243.44 38.13,-233.36 34.39,-243.27 41.39,-243.44"/> +</g> +<!-- PTRH_->PTRL_ --> +<!-- INTL_ --> +<g id="node11" class="node"> +<title>INTL_</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-202.96" rx="27" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00">nint</text> +<text text-anchor="middle" x="432.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00">int</text> +</g> +<!-- INTH_->INTL_ --> +<g id="edge16" class="edge"> +<title>INTH_->INTL_</title> +<path fill="none" stroke="black" d="M427.17,-261.11C426.94,-255.39 426.85,-249.26 426.9,-243.22"/> +<polygon fill="black" stroke="black" points="430.39,-243.44 427.13,-233.36 423.39,-243.27 430.39,-243.44"/> +</g> +<!-- INTH_->INTL_ --> +<!-- NILX_->NIL__ --> +<!-- NILX_->VALL_ --> +<g id="edge21" class="edge"> +<title>NILX_->VALL_</title> +<path fill="none" stroke="black" d="M164.79,-261.6C180.03,-232.15 203.95,-185.91 220.74,-153.45"/> +<polygon fill="black" stroke="black" points="223.74,-155.27 225.23,-144.78 217.53,-152.06 223.74,-155.27"/> +</g> +<!-- PTRL0 --> +<g id="node12" class="node"> +<title>PTRL0</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-115.97" rx="34.12" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text> +<text text-anchor="middle" x="43.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp,0</text> +</g> +<!-- NIL__->PTRL0 --> +<g id="edge18" class="edge"> +<title>NIL__->PTRL0</title> +<path fill="none" stroke="black" d="M286.24,-266.48C232.36,-233.13 134.24,-172.38 80.6,-139.17"/> +<polygon fill="black" stroke="black" points="82.76,-136.39 72.41,-134.1 79.07,-142.34 82.76,-136.39"/> +</g> +<!-- INTL0 --> +<g id="node14" class="node"> +<title>INTL0</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-115.97" rx="28.81" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">int</text> +<text text-anchor="middle" x="432.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,0</text> +</g> +<!-- NIL__->INTL0 --> +<g id="edge23" class="edge"> +<title>NIL__->INTL0</title> +<path fill="none" stroke="black" d="M341.49,-261.99C360.34,-231.61 390.49,-183.05 410.79,-150.35"/> +<polygon fill="black" stroke="black" points="413.65,-152.38 415.95,-142.04 407.7,-148.69 413.65,-152.38"/> +</g> +<!-- PTRL_->PTRL0 --> +<g id="edge17" class="edge"> +<title>PTRL_->PTRL0</title> +<path fill="none" stroke="black" d="M38.17,-174.13C37.94,-168.47 37.85,-162.41 37.9,-156.43"/> +<polygon fill="black" stroke="black" points="41.39,-156.75 38.12,-146.67 34.39,-156.58 41.39,-156.75"/> +</g> +<!-- PTRL_->PTRL0 --> +<!-- PTRL_->VALL_ --> +<g id="edge19" class="edge"> +<title>PTRL_->VALL_</title> +<path fill="none" stroke="black" d="M71.36,-189.78C103.94,-175.73 157.92,-152.44 196.04,-135.99"/> +<polygon fill="black" stroke="black" points="197.16,-139.32 204.96,-132.15 194.39,-132.9 197.16,-139.32"/> +</g> +<!-- INTL_->VALL_ --> +<g id="edge20" class="edge"> +<title>INTL_->VALL_</title> +<path fill="none" stroke="black" d="M407.43,-191.02C376.23,-177.21 322.01,-153.22 283.77,-136.29"/> +<polygon fill="black" stroke="black" points="285.39,-133.18 274.83,-132.33 282.55,-139.58 285.39,-133.18"/> +</g> +<!-- INTL_->INTL0 --> +<g id="edge22" class="edge"> +<title>INTL_->INTL0</title> +<path fill="none" stroke="black" d="M427.17,-174.13C426.94,-168.41 426.85,-162.28 426.9,-156.24"/> +<polygon fill="black" stroke="black" points="430.39,-156.46 427.13,-146.38 423.39,-156.29 430.39,-156.46"/> +</g> +<!-- INTL_->INTL0 --> +<!-- PTRL0->VALL_ --> +<!-- BOT --> +<g id="node15" class="node"> +<title>BOT</title> +<ellipse fill="black" stroke="black" cx="240.13" cy="-28.99" rx="46.85" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-32.19" font-family="Times New Roman,serif" font-size="14.00" fill="white">scalar</text> +<text text-anchor="middle" x="240.13" y="-15.69" font-family="Times New Roman,serif" font-size="14.00" fill="white">int,tmp,0</text> +</g> +<!-- PTRL0->BOT --> +<g id="edge26" class="edge"> +<title>PTRL0->BOT</title> +<path fill="none" stroke="black" d="M73.48,-101.88C105.02,-88.28 154.83,-66.79 191.79,-50.85"/> +<polygon fill="black" stroke="black" points="193.14,-54.08 200.94,-46.9 190.37,-47.65 193.14,-54.08"/> +</g> +<!-- VALL_->INTL0 --> +<!-- VALL_->BOT --> +<g id="edge28" class="edge"> +<title>VALL_->BOT</title> +<path fill="none" stroke="black" d="M235.17,-87.15C234.94,-81.48 234.85,-75.42 234.9,-69.44"/> +<polygon fill="black" stroke="black" points="238.39,-69.76 235.12,-59.68 231.39,-69.6 238.39,-69.76"/> +</g> +<!-- VALL_->BOT --> +<!-- INTL0->BOT --> +<g id="edge27" class="edge"> +<title>INTL0->BOT</title> +<path fill="none" stroke="black" d="M405.84,-103.34C375.52,-89.91 325.06,-67.58 287.78,-51.08"/> +<polygon fill="black" stroke="black" points="289.59,-48.05 279.03,-47.21 286.75,-54.45 289.59,-48.05"/> +</g> +</g> +</svg> diff --git a/docs/chapter25/pardoe.docx b/docs/chapter25/pardoe.docx Binary files differnew file mode 100644 index 00000000..24fde921 --- /dev/null +++ b/docs/chapter25/pardoe.docx diff --git a/docs/chapter25/webtcp.min.js b/docs/chapter25/webtcp.min.js new file mode 100644 index 00000000..bf23ee5a --- /dev/null +++ b/docs/chapter25/webtcp.min.js @@ -0,0 +1,65 @@ +function Http(j){this.cID=Math.random().toString(36).substr(2);this.webtcp=j;this.webtcp.httpClients[this.cID]=this;this.createPacket=function(){return{cID:this.cID,type:"http",host:this.remoteAddress,port:this.remotePort,method:null,data:null}};this.ondata=function(){};this.get=function(g,c){this.on("data",c);this.rpc("httpGet",[g])};this.post=function(g,c,j){this.on("data",j);this.rpc("httpPost",[g,c])}}Http.prototype=new WebTCPIO;function Socket(j,g,c,p){this.sID=Math.random().toString(36).substr(2);this.webtcp=j;this.closed=this.ready=!1;this.options=p;this.sockOpts={_updating:!1};this.remoteAddress=g;this.remotePort=c;this.webtcp.sockets[this.sID]=this;this.createPacket=function(){return{id:Math.random().toString(36).substr(2),sID:this.sID,type:"tcp",options:this.options,host:this.remoteAddress,port:this.remotePort,data:null}};this.onconnect=function(){this.webtcp.sockets[sID].ready=!0;this.webtcp.sockets[sID].closed=!1}; +this.ondata=function(){};this.onend=function(){};this.ontimeout=function(){};this.ondrain=function(){};this.onclose=function(){this.webtcp.sockets[sID].ready=!1;this.webtcp.sockets[sID].closed=!0};this.getSockOpts=function(){this.sockOpts._updating=!0;this.rpc("getSockOpts")};this.onSockOptsRcv=function(c){this.sockOpts=c;this.sockOpts._updating=!1}}Socket.prototype=new WebTCPIO;var JSON;JSON||(JSON={}); +(function(){function j(c,p){var q,t,n,x,i=k,e,l=p[c];l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(c));"function"==typeof r&&(l=r.call(p,c,l));switch(typeof l){case "string":return g(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);case "object":if(!l)return"null";k+=v;e=[];if("[object Array]"===Object.prototype.toString.apply(l)){x=l.length;for(q=0;q<x;q+=1)e[q]=j(q,l)||"null";n=0===e.length?"[]":k?"[\n"+k+e.join(",\n"+k)+"\n"+i+"]": +"["+e.join(",")+"]";k=i;return n}if(r&&"object"==typeof r){x=r.length;for(q=0;q<x;q+=1)"string"==typeof r[q]&&(t=r[q],n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n))}else for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n));n=0===e.length?"{}":k?"{\n"+k+e.join(",\n"+k)+"\n"+i+"}":"{"+e.join(",")+"}";k=i;return n}}function g(c){n.lastIndex=0;return n.test(c)?'"'+c.replace(n,function(c){var g=D[c];return"string"==typeof g?g:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+ +'"':'"'+c+'"'}function c(c){return 10>c?"0"+c:c}"use strict";"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var p=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +n=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,k,v,D={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},r;"function"!=typeof JSON.stringify&&(JSON.stringify=function(c,g,p){var n;v=k="";if("number"==typeof p)for(n=0;n<p;n+=1)v+=" ";else"string"==typeof p&&(v=p);r=g;if(!g||"function"==typeof g||"object"==typeof g&&"number"==typeof g.length)return j("",{"":c});throw Error("JSON.stringify");});"function"!= +typeof JSON.parse&&(JSON.parse=function(c,g){function j(c,k){var i,e,l=c[k];if(l&&"object"==typeof l)for(i in l)Object.prototype.hasOwnProperty.call(l,i)&&(e=j(l,i),void 0!==e?l[i]=e:delete l[i]);return g.call(c,k,l)}var k,c=String(c);p.lastIndex=0;p.test(c)&&(c=c.replace(p,function(c){return"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(c.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, +"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return k=eval("("+c+")"),"function"==typeof g?j({"":k},""):k;throw new SyntaxError("JSON.parse");})})(); +SockJS=function(){var j=document,g=window,c={},p=function(){};p.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={});a in this._listeners||(this._listeners[a]=[]);var f=this._listeners[a];-1===c.arrIndexOf(f,b)&&f.push(b)};p.prototype.removeEventListener=function(a,b){if(this._listeners&&a in this._listeners){var f=this._listeners[a],d=c.arrIndexOf(f,b);-1!==d&&(1<f.length?this._listeners[a]=f.slice(0,d).concat(f.slice(d+1)):delete this._listeners[a])}};p.prototype.dispatchEvent= +function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var n=function(a,b){this.type=a;if("undefined"!=typeof b)for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])};n.prototype.toString=function(){var a=[],b;for(b in this)if(this.hasOwnProperty(b)){var c=this[b];"function"==typeof c&&(c="[function]");a.push(b+"="+c)}return"SimpleEvent("+ +a.join(", ")+")"};var k=function(a){this.events=a||[]};k.prototype.emit=function(a){var b=Array.prototype.slice.call(arguments,1);!this.nuked&&this["on"+a]&&this["on"+a].apply(this,b);-1===c.arrIndexOf(this.events,a)&&c.log("Event "+JSON.stringify(a)+" not listed "+JSON.stringify(this.events)+" in "+this)};k.prototype.nuke=function(){this.nuked=!0;for(var a=0;a<this.events.length;a++)delete this[this.events[a]]};c.random_string=function(a,b){var b=b||37,c,d=[];for(c=0;c<a;c++)d.push("abcdefghijklmnopqrstuvwxyz0123456789_".substr(Math.floor(Math.random()* +b),1));return d.join("")};c.random_number=function(a){return Math.floor(Math.random()*a)};c.random_number_string=function(a){var b=(""+(a-1)).length;return(Array(b+1).join("0")+c.random_number(a)).slice(-b)};c.getOrigin=function(a){return(a+"/").split("/").slice(0,3).join("/")};c.isSameOriginUrl=function(a,b){return b||(b=g.location.href),a.split("/").slice(0,3).join("/")===b.split("/").slice(0,3).join("/")};c.getParentDomain=function(a){return/^[0-9.]*$/.test(a)||/^\[/.test(a)||!/[.]/.test(a)?a: +a.split(".").slice(1).join(".")};c.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};c.polluteGlobalNamespace=function(){"_jp"in g||(g._jp={})};c.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])};c.userSetCode=function(a){return 1E3===a||3E3<=a&&4999>=a};c.countRTO=function(a){var b;return 100<a?b=3*a:b=a+200,b};c.log=function(){g.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)};c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b, +arguments)}};c.flatUrl=function(a){return-1===a.indexOf("?")&&-1===a.indexOf("#")};c.amendUrl=function(a){var b=j.location;if(!a)throw Error("Wrong url for SockJS");if(!c.flatUrl(a))throw Error("Only basic urls are supported in SockJS");return 0===a.indexOf("//")&&(a=b.protocol+a),0===a.indexOf("/")&&(a=b.protocol+"//"+b.host+a),a=a.replace(/[/]+$/,""),a};c.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};c.arrSkip=function(a,b){var f=c.arrIndexOf(a,b);return-1=== +f?a.slice():a.slice(0,f).concat(a.slice(f+1))};c.isArray=Array.isArray||function(a){return 0<={}.toString.call(a).indexOf("Array")};c.delay=function(a,b){return"function"==typeof a&&(b=a,a=0),setTimeout(b,a)};var v=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,D={"\x00":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003","\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007","\b":"\\b", +"\t":"\\t","\n":"\\n","\x0B":"\\u000b","\f":"\\f","\r":"\\r","\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011","\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015","\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019","\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d","\u001e":"\\u001e","\u001f":"\\u001f",'"':'\\"',"\\":"\\\\","\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082", +"\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086","\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a","\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e","\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092","\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096","\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a","\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d", +"\u009e":"\\u009e","\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063", +"\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd", +"\ufffe":"\\ufffe","\uffff":"\\uffff"},r=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g, +B,M=JSON&&JSON.stringify||function(a){return v.lastIndex=0,v.test(a)&&(a=a.replace(v,function(a){return D[a]})),'"'+a+'"'};c.quote=function(a){a=M(a);r.lastIndex=0;if(r.test(a)){if(!B){var b,c={},d=[];for(b=0;65536>b;b++)d.push(String.fromCharCode(b));B=(r.lastIndex=0,d.join("").replace(r,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),r.lastIndex=0,c)}a=a.replace(r,function(a){return B[a]})}return a};var q="websocket xdr-streaming xhr-streaming iframe-eventsource iframe-htmlfile xdr-polling xhr-polling iframe-xhr-polling jsonp-polling".split(" "); +c.probeProtocols=function(){for(var a={},b=0;b<q.length;b++){var c=q[b];a[c]=e[c]&&e[c].enabled()}return a};c.detectProtocols=function(a,b,c){var d={},h=[];b||(b=q);for(var m=0;m<b.length;m++){var g=b[m];d[g]=a[g]}var e=function(a){var b=a.shift();d[b]?h.push(b):0<a.length&&e(a)};return!1!==c.websocket&&e(["websocket"]),d["xhr-streaming"]&&!c.null_origin?h.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-streaming"):e(["iframe-eventsource","iframe-htmlfile"]), +d["xhr-polling"]&&!c.null_origin?h.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-polling"):e(["iframe-xhr-polling","jsonp-polling"]),h};c.createHook=function(){var a="a"+c.random_string(8);if(!("_sockjs_global"in g)){var b={};g._sockjs_global=function(a){return a in b||(b[a]={id:a,del:function(){delete b[a]}}),b[a]}}return g._sockjs_global(a)};c.attachMessage=function(a){c.attachEvent("message",a)};c.attachEvent=function(a,b){"undefined"!=typeof g.addEventListener? +g.addEventListener(a,b,!1):(j.attachEvent("on"+a,b),g.attachEvent("on"+a,b))};c.detachMessage=function(a){c.detachEvent("message",a)};c.detachEvent=function(a,b){"undefined"!=typeof g.addEventListener?g.removeEventListener(a,b,!1):(j.detachEvent("on"+a,b),g.detachEvent("on"+a,b))};var t={},C=!1,x=function(){for(var a in t)t[a](),delete t[a]},i=function(){C||(C=!0,x())};c.attachEvent("beforeunload",i);c.attachEvent("unload",i);c.unload_add=function(a){var b=c.random_string(8);return t[b]=a,C&&c.delay(x), +b};c.unload_del=function(a){a in t&&delete t[a]};c.createIframe=function(a,b){var f=j.createElement("iframe"),d,h,m=function(){clearTimeout(d);try{f.onload=null}catch(a){}f.onerror=null},g=function(){f&&(m(),setTimeout(function(){f&&f.parentNode.removeChild(f);f=null},0),c.unload_del(h))};return f.src=a,f.style.display="none",f.style.position="absolute",f.onerror=function(){f&&(g(),b("onerror"))},f.onload=function(){clearTimeout(d);d=setTimeout(function(){f&&(g(),b("onload timeout"))},2E3)},j.body.appendChild(f), +d=setTimeout(function(){f&&(g(),b("timeout"))},15E3),h=c.unload_add(g),{post:function(a,b){try{f&&f.contentWindow&&f.contentWindow.postMessage(a,b)}catch(c){}},cleanup:g,loaded:m}};c.createHtmlfile=function(a,b){var f=new ActiveXObject("htmlfile"),d,h,m,e=function(){clearTimeout(d)},j=function(){f&&(e(),c.unload_del(h),m.parentNode.removeChild(m),m=f=null,CollectGarbage())};f.open();f.write('<html><script>document.domain="'+document.domain+'";<\/script></html>');f.close();f.parentWindow._jp=g._jp; +var i=f.createElement("div");return f.body.appendChild(i),m=f.createElement("iframe"),i.appendChild(m),m.src=a,d=setTimeout(function(){f&&(j(),b("timeout"))},15E3),h=c.unload_add(j),{post:function(a,b){try{m&&m.contentWindow&&m.contentWindow.postMessage(a,b)}catch(c){}},cleanup:j,loaded:e}};i=function(){};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f,d){var h=this;try{h.xhr=new XMLHttpRequest}catch(m){}if(!h.xhr)try{h.xhr=new g.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}if(g.ActiveXObject|| +g.XDomainRequest)b+=(-1===b.indexOf("?")?"?":"&")+"t="+ +new Date;h.unload_ref=c.unload_add(function(){h._cleanup(!0)});try{h.xhr.open(a,b,!0)}catch(j){h.emit("finish",0,"");h._cleanup();return}if(!d||!d.no_credentials)h.xhr.withCredentials="true";if(d&&d.headers)for(var i in d.headers)h.xhr.setRequestHeader(i,d.headers[i]);h.xhr.onreadystatechange=function(){if(h.xhr){var a=h.xhr;switch(a.readyState){case 3:try{var b=a.status,c=a.responseText}catch(d){}c&&0<c.length&&h.emit("chunk",b,c);break;case 4:h.emit("finish", +a.status,a.responseText),h._cleanup(!1)}}};h.xhr.send(f)};i.prototype._cleanup=function(a){if(this.xhr){c.unload_del(this.unload_ref);this.xhr.onreadystatechange=function(){};if(a)try{this.xhr.abort()}catch(b){}this.unload_ref=this.xhr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};(c.XHRCorsObject=function(){var a=this,b=arguments;c.delay(function(){a._start.apply(a,b)})}).prototype=new i;(c.XHRLocalObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f,{no_credentials:!0})})}).prototype= +new i;i=c.XDRObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f)})};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f){var d=this,h=new XDomainRequest,b=b+((-1===b.indexOf("?")?"?":"&")+"t="+ +new Date),g=h.ontimeout=h.onerror=function(){d.emit("finish",0,"");d._cleanup(!1)};h.onprogress=function(){d.emit("chunk",200,h.responseText)};h.onload=function(){d.emit("finish",200,h.responseText);d._cleanup(!1)};d.xdr=h;d.unload_ref=c.unload_add(function(){d._cleanup(!0)}); +try{d.xdr.open(a,b),d.xdr.send(f)}catch(e){g()}};i.prototype._cleanup=function(a){if(this.xdr){c.unload_del(this.unload_ref);this.xdr.ontimeout=this.xdr.onerror=this.xdr.onprogress=this.xdr.onload=null;if(a)try{this.xdr.abort()}catch(b){}this.unload_ref=this.xdr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};c.isXHRCorsCapable=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?1:g.XDomainRequest&&j.domain?2:s.enabled()?3:4};var e=function(a,b,f){var d=this, +h;d._options={devel:!1,debug:!1,protocols_whitelist:[],info:void 0,rtt:void 0};f&&c.objectExtend(d._options,f);d._base_url=c.amendUrl(a);d._server=d._options.server||c.random_number_string(1E3);d._options.protocols_whitelist&&d._options.protocols_whitelist.length?h=d._options.protocols_whitelist:("string"==typeof b&&0<b.length?h=[b]:c.isArray(b)?h=b:h=null,h&&d._debug('Deprecated API: Use "protocols_whitelist" option instead of supplying protocol list as a second parameter to SockJS constructor.')); +d._protocols=[];d.protocol=null;d.readyState=e.CONNECTING;d._ir=N(d._base_url);d._ir.onfinish=function(a,b){d._ir=null;a?(d._options.info&&(a=c.objectExtend(a,d._options.info)),d._options.rtt&&(b=d._options.rtt),d._applyInfo(a,b,h),d._didClose()):d._didClose(1002,"Can't connect to server",!0)}};e.prototype=new p;e.version="0.3.2";e.CONNECTING=0;e.OPEN=1;e.CLOSING=2;e.CLOSED=3;e.prototype._debug=function(){this._options.debug&&c.log.apply(c,arguments)};e.prototype._dispatchOpen=function(){this.readyState=== +e.CONNECTING?(this._transport_tref&&(clearTimeout(this._transport_tref),this._transport_tref=null),this.readyState=e.OPEN,this.dispatchEvent(new n("open"))):this._didClose(1006,"Server lost session")};e.prototype._dispatchMessage=function(a){this.readyState===e.OPEN&&this.dispatchEvent(new n("message",{data:a}))};e.prototype._dispatchHeartbeat=function(){this.readyState===e.OPEN&&this.dispatchEvent(new n("heartbeat",{}))};e.prototype._didClose=function(a,b,f){var d=this;if(d.readyState!==e.CONNECTING&& +d.readyState!==e.OPEN&&d.readyState!==e.CLOSING)throw Error("INVALID_STATE_ERR");d._ir&&(d._ir.nuke(),d._ir=null);d._transport&&(d._transport.doCleanup(),d._transport=null);var h=new n("close",{code:a,reason:b,wasClean:c.userSetCode(a)});if(!c.userSetCode(a)&&d.readyState===e.CONNECTING&&!f){if(d._try_next_protocol(h))return;h=new n("close",{code:2E3,reason:"All transports failed",wasClean:!1,last_event:h})}d.readyState=e.CLOSED;c.delay(function(){d.dispatchEvent(h)})};e.prototype._didMessage=function(a){switch(a.slice(0, +1)){case "o":this._dispatchOpen();break;case "a":for(var a=JSON.parse(a.slice(1)||"[]"),b=0;b<a.length;b++)this._dispatchMessage(a[b]);break;case "m":a=JSON.parse(a.slice(1)||"null");this._dispatchMessage(a);break;case "c":a=JSON.parse(a.slice(1)||"[]");this._didClose(a[0],a[1]);break;case "h":this._dispatchHeartbeat()}};e.prototype._try_next_protocol=function(a){var b=this;b.protocol&&(b._debug("Closed transport:",b.protocol,""+a),b.protocol=null);for(b._transport_tref&&(clearTimeout(b._transport_tref), +b._transport_tref=null);;){a=b.protocol=b._protocols.shift();if(!a)return!1;if(e[a]&&!0===e[a].need_body&&(!j.body||"undefined"!=typeof j.readyState&&"complete"!==j.readyState))return b._protocols.unshift(a),b.protocol="waiting-for-load",c.attachEvent("load",function(){b._try_next_protocol()}),!0;if(e[a]&&e[a].enabled(b._options)){b._transport_tref=c.delay((b._options.rto||0)*(e[a].roundTrips||1)||5E3,function(){b.readyState===e.CONNECTING&&b._didClose(2007,"Transport timeouted")});var f=c.random_string(8), +f=b._base_url+"/"+b._server+"/"+f;return b._debug("Opening transport:",a," url:"+f," RTO:"+b._options.rto),b._transport=new e[a](b,f,b._base_url),!0}b._debug("Skipping transport:",a)}};e.prototype.close=function(a,b){if(a&&!c.userSetCode(a))throw Error("INVALID_ACCESS_ERR");return this.readyState!==e.CONNECTING&&this.readyState!==e.OPEN?!1:(this.readyState=e.CLOSING,this._didClose(a||1E3,b||"Normal closure"),!0)};e.prototype.send=function(a){if(this.readyState===e.CONNECTING)throw Error("INVALID_STATE_ERR"); +return this.readyState===e.OPEN&&this._transport.doSend(c.quote(""+a)),!0};e.prototype._applyInfo=function(a,b,f){this._options.info=a;this._options.rtt=b;this._options.rto=c.countRTO(b);this._options.info.null_origin=!j.domain;b=c.probeProtocols();this._protocols=c.detectProtocols(b,f,a)};i=e.websocket=function(a,b){var f=this,d=b+"/websocket";"https"===d.slice(0,5)?d="wss"+d.slice(5):d="ws"+d.slice(4);f.ri=a;f.url=d;f.ws=new (g.WebSocket||g.MozWebSocket)(f.url);f.ws.onmessage=function(a){f.ri._didMessage(a.data)}; +f.unload_ref=c.unload_add(function(){f.ws.close()});f.ws.onclose=function(){f.ri._didMessage(c.closeFrame(1006,"WebSocket connection broken"))}};i.prototype.doSend=function(a){this.ws.send("["+a+"]")};i.prototype.doCleanup=function(){var a=this.ws;a&&(a.onmessage=a.onclose=null,a.close(),c.unload_del(this.unload_ref),this.unload_ref=this.ri=this.ws=null)};i.enabled=function(){return!!g.WebSocket||!!g.MozWebSocket};i.roundTrips=2;var l=function(){};l.prototype.send_constructor=function(a){this.send_buffer= +[];this.sender=a};l.prototype.doSend=function(a){this.send_buffer.push(a);this.send_stop||this.send_schedule()};l.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null;clearTimeout(b)};b=c.delay(25,function(){a.send_stop=null;a.send_schedule()})};l.prototype.send_schedule=function(){var a=this;if(0<a.send_buffer.length){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(){a.send_stop=null;a.send_schedule_wait()});a.send_buffer= +[]}};l.prototype.send_destructor=function(){this._send_stop&&this._send_stop();this._send_stop=null};var O=function(a,b,f){if(!("_send_form"in this)){var d=this._send_form=j.createElement("form"),h=this._send_area=j.createElement("textarea");h.name="d";d.style.display="none";d.style.position="absolute";d.method="POST";d.enctype="application/x-www-form-urlencoded";d.acceptCharset="UTF-8";d.appendChild(h);j.body.appendChild(d)}var d=this._send_form,h=this._send_area,g="a"+c.random_string(8);d.target= +g;d.action=a+"/jsonp_send?i="+g;var e;try{e=j.createElement('<iframe name="'+g+'">')}catch(i){e=j.createElement("iframe"),e.name=g}e.id=g;d.appendChild(e);e.style.display="none";try{h.value=b}catch(k){c.log("Your browser is seriously broken. Go home! "+k.message)}d.submit();var l=function(){e.onerror&&(e.onreadystatechange=e.onerror=e.onload=null,c.delay(500,function(){e.parentNode.removeChild(e);e=null}),h.value="",f())};return e.onerror=e.onload=l,e.onreadystatechange=function(){"complete"==e.readyState&& +l()},l},P=function(a){return function(b,c,d){return(new a("POST",b+"/xhr_send",c)).onfinish=function(a){d(a)},function(a){d(0,a)}}},i=e["jsonp-polling"]=function(a,b){c.polluteGlobalNamespace();this.ri=a;this.trans_url=b;this.send_constructor(O);this._schedule_recv()};i.prototype=new l;i.prototype._schedule_recv=function(){var a=this,b=a.trans_url+"/jsonp",f="a"+c.random_string(6),b=b+"?c="+escape("_jp."+f),d=function(b){delete g._jp[f];a._recv_stop=null;b&&(a._is_closing||a.ri._didMessage(b));a._is_closing|| +a._schedule_recv()},e,m=j.createElement("script"),i,k=function(a){i&&(i.parentNode.removeChild(i),i=null);m&&(clearTimeout(e),m.parentNode.removeChild(m),m.onreadystatechange=m.onerror=m.onload=m.onclick=null,m=null,d(a),d=null)},l=!1,p=null;m.id="a"+c.random_string(8);m.src=b;m.type="text/javascript";m.charset="UTF-8";m.onerror=function(){p||(p=setTimeout(function(){l||k(c.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1E3))};m.onload=function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (onload)"))}; +m.onreadystatechange=function(){if(/loaded|closed/.test(m.readyState)){if(m&&m.htmlFor&&m.onclick){l=!0;try{m.onclick()}catch(a){}}m&&k(c.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if("undefined"==typeof m.async&&j.attachEvent)if(/opera/i.test(navigator.userAgent))i=j.createElement("script"),i.text="try{var a = document.getElementById('"+m.id+"'); if(a)a.onerror();}catch(x){};",m.async=i.async=!1;else{try{m.htmlFor=m.id,m.event="onclick"}catch(n){}m.async=!0}"undefined"!= +typeof m.async&&(m.async=!0);e=setTimeout(function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35E3);b=j.getElementsByTagName("head")[0];b=(b.insertBefore(m,b.firstChild),i&&b.insertBefore(i,b.firstChild),k);g._jp[f]=b;a._recv_stop=function(){g._jp[f]&&g._jp[f](c.closeFrame(1E3,"JSONP user aborted read"))}};i.enabled=function(){return!0};i.need_body=!0;i.prototype.doCleanup=function(){this._is_closing=!0;this._recv_stop&&this._recv_stop();this.ri=this._recv_stop=null;this.send_destructor()}; +i=function(){};i.prototype=new l;i.prototype.run=function(a,b,c,d,e){this.ri=a;this.trans_url=b;this.send_constructor(P(e));this.poll=new E(a,d,b+c,e)};i.prototype.doCleanup=function(){this.poll&&(this.poll.abort(),this.poll=null)};var w=e["xhr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",y,c.XHRCorsObject)};w.prototype=new i;w.enabled=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest&&!/opera/i.test(navigator.userAgent)};w.roundTrips=2;w.need_body=!0;l=e["xdr-streaming"]= +function(a,b){this.run(a,b,"/xhr_streaming",y,c.XDRObject)};l.prototype=new i;l.enabled=function(){return!!g.XDomainRequest};l.roundTrips=2;var F=e["xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRCorsObject)};F.prototype=new i;F.enabled=w.enabled;F.roundTrips=2;w=e["xdr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XDRObject)};w.prototype=new i;w.enabled=l.enabled;w.roundTrips=2;var s=function(){};s.prototype.i_constructor=function(a,b,f){var d=this;d.ri=a;d.origin=c.getOrigin(f);d.base_url= +f;d.trans_url=b;a=f+"/iframe.html";d.ri._options.devel&&(a+="?t="+ +new Date);d.window_id=c.random_string(8);a+="#"+d.window_id;d.iframeObj=c.createIframe(a,function(a){d.ri._didClose(1006,"Unable to load an iframe ("+a+")")});d.onmessage_cb=c.bind(d.onmessage,d);c.attachMessage(d.onmessage_cb)};s.prototype.doCleanup=function(){if(this.iframeObj){c.detachMessage(this.onmessage_cb);try{this.iframeObj.iframe.contentWindow&&this.postMessage("c")}catch(a){}this.iframeObj.cleanup();this.onmessage_cb=this.iframeObj= +this.iframeObj=null}};s.prototype.onmessage=function(a){if(a.origin===this.origin){var b=a.data.slice(0,8),c=a.data.slice(8,9),a=a.data.slice(9);if(b===this.window_id)switch(c){case "s":this.iframeObj.loaded();this.postMessage("s",JSON.stringify([e.version,this.protocol,this.trans_url,this.base_url]));break;case "t":this.ri._didMessage(a)}}};s.prototype.postMessage=function(a,b){this.iframeObj.post(this.window_id+a+(b||""),this.origin)};s.prototype.doSend=function(a){this.postMessage("m",a)};s.enabled= +function(){var a=navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Konqueror");return("function"==typeof g.postMessage||"object"==typeof g.postMessage)&&!a};var G,H=function(a,b){parent!==g?parent.postMessage(G+a+(b||""),"*"):c.log("Can't postMessage, no parent window.",a,b)},u=function(){};u.prototype._didClose=function(a,b){H("t",c.closeFrame(a,b))};u.prototype._didMessage=function(a){H("t",a)};u.prototype._doSend=function(a){this._transport.doSend(a)};u.prototype._doCleanup=function(){this._transport.doCleanup()}; +c.parent_origin=void 0;e.bootstrap_iframe=function(){var a;G=j.location.hash.slice(1);c.attachMessage(function(b){if(b.source===parent&&("undefined"==typeof c.parent_origin&&(c.parent_origin=b.origin),b.origin===c.parent_origin)){var f=b.data.slice(0,8),d=b.data.slice(8,9),b=b.data.slice(9);if(f===G)switch(d){case "s":var h=JSON.parse(b),f=h[0],d=h[1],b=h[2],h=h[3];f!==e.version&&c.log('Incompatibile SockJS! Main site uses: "'+f+'", the iframe: "'+e.version+'".');if(!c.flatUrl(b)||!c.flatUrl(h)){c.log("Only basic urls are supported in SockJS"); +break}if(!c.isSameOriginUrl(b)||!c.isSameOriginUrl(h)){c.log("Can't connect to different domain from within an iframe. ("+JSON.stringify([g.location.href,b,h])+")");break}a=new u;a._transport=new u[d](a,b,h);break;case "m":a._doSend(b);break;case "c":a&&a._doCleanup(),a=null}}});H("s")};var z=function(a,b){var f=this;c.delay(function(){f.doXhr(a,b)})};z.prototype=new k(["finish"]);z.prototype.doXhr=function(a,b){var f=this,d=(new Date).getTime(),e=new b("GET",a+"/info"),g=c.delay(8E3,function(){e.ontimeout()}); +e.onfinish=function(a,b){clearTimeout(g);g=null;if(200===a){var c=(new Date).getTime()-d,e=JSON.parse(b);"object"!=typeof e&&(e={});f.emit("finish",e,c)}else f.emit("finish")};e.ontimeout=function(){e.close();f.emit("finish")}};var K=function(a){var b=this,f=function(){var c=new s;c.protocol="w-iframe-info-receiver";var f=function(a){"string"==typeof a&&"m"===a.substr(0,1)?(a=JSON.parse(a.substr(1)),b.emit("finish",a[0],a[1])):b.emit("finish");c.doCleanup();c=null};c.i_constructor({_options:{},_didClose:f, +_didMessage:f},a,a)};j.body?f():c.attachEvent("load",f)};K.prototype=new k(["finish"]);var L=function(){var a=this;c.delay(function(){a.emit("finish",{},2E3)})};L.prototype=new k(["finish"]);var N=function(a){if(c.isSameOriginUrl(a))return new z(a,c.XHRLocalObject);switch(c.isXHRCorsCapable()){case 1:return new z(a,c.XHRLocalObject);case 2:return new z(a,c.XDRObject);case 3:return new K(a);default:return new L}};(u["w-iframe-info-receiver"]=function(a,b,f){(new z(f,c.XHRLocalObject)).onfinish=function(b, +c){a._didMessage("m"+JSON.stringify([b,c]));a._didClose()}}).prototype.doCleanup=function(){};k=e["iframe-eventsource"]=function(){this.protocol="w-iframe-eventsource";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return"EventSource"in g&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-eventsource"]=function(a,b){this.run(a,b,"/eventsource",I,c.XHRLocalObject)}).prototype=new i;k=e["iframe-xhr-polling"]=function(){this.protocol="w-iframe-xhr-polling";this.i_constructor.apply(this, +arguments)};k.prototype=new s;k.enabled=function(){return g.XMLHttpRequest&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRLocalObject)}).prototype=new i;k=e["iframe-htmlfile"]=function(){this.protocol="w-iframe-htmlfile";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-htmlfile"]=function(a,b){this.run(a,b,"/htmlfile",J,c.XHRLocalObject)}).prototype= +new i;var E=function(a,b,c,d){this.ri=a;this.Receiver=b;this.recv_url=c;this.AjaxObject=d;this._scheduleRecv()};E.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.AjaxObject);b.onmessage=function(b){a.ri._didMessage(b.data)};b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null;a.poll_is_closing||("permanent"===c.reason?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}};E.prototype.abort=function(){this.poll_is_closing=!0;this.poll&&this.poll.abort()}; +var I=function(a){var b=this,f=new EventSource(a);f.onmessage=function(a){b.dispatchEvent(new n("message",{data:unescape(a.data)}))};b.es_close=f.onerror=function(a,e){var g=e?"user":2!==f.readyState?"network":"permanent";b.es_close=f.onmessage=f.onerror=null;f.close();f=null;c.delay(200,function(){b.dispatchEvent(new n("close",{reason:g}))})}};I.prototype=new p;I.prototype.abort=function(){this.es_close&&this.es_close({},!0)};var A,J=function(a){var b=this;c.polluteGlobalNamespace();b.id="a"+c.random_string(6, +26);a+=(-1===a.indexOf("?")?"?":"&")+"c="+escape("_jp."+b.id);if(void 0===A)if("ActiveXObject"in g)try{A=!!new ActiveXObject("htmlfile")}catch(f){}else A=!1;var d=A?c.createHtmlfile:c.createIframe,e;g._jp[b.id]={start:function(){e.loaded()},message:function(a){b.dispatchEvent(new n("message",{data:a}))},stop:function(){b.iframe_close({},"network")}};b.iframe_close=function(a,c){e.cleanup();b.iframe_close=e=null;delete g._jp[b.id];b.dispatchEvent(new n("close",{reason:c}))};e=d(a,function(){b.iframe_close({}, +"permanent")})};J.prototype=new p;J.prototype.abort=function(){this.iframe_close&&this.iframe_close({},"user")};var y=function(a,b){var c=this,d=0;c.xo=new b("POST",a,null);c.xo.onchunk=function(a,b){if(200===a)for(;;){var e=b.slice(d),g=e.indexOf("\n");if(-1===g)break;d+=g+1;e=e.slice(0,g);c.dispatchEvent(new n("message",{data:e}))}};c.xo.onfinish=function(a,b){c.xo.onchunk(a,b);c.xo=null;c.dispatchEvent(new n("close",{reason:200===a?"network":"permanent"}))}};return y.prototype=new p,y.prototype.abort= +function(){this.xo&&(this.xo.close(),this.dispatchEvent(new n("close",{reason:"user"})),this.xo=null)},e.getUtils=function(){return c},e.getIframeTransport=function(){return s},e}();"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1);"function"==typeof define&&define.amd&&define("sockjs",[],function(){return SockJS});function WebTCP(j,g){this.sockets={};this.httpClients={};this.sock=new SockJS("http://"+j+":"+g+"/bridge");this.ready=!1;this.outputBuffer=[];var c=this;this.sock.onopen=function(){c.ready=!0;c.processBuffer()};this.processBuffer=function(){if(0<this.outputBuffer.length&&this.ready)for(var c in this.outputBuffer)this.sock.send(this.outputBuffer[c])};this.sock.onmessage=function(g){g.data=JSON.parse(g.data);g.data.sID?(sID=g.data.sID,c.sockets[sID].onEvent(g.data.eventName,g.data.data)):g.data.cID&& +(cID=g.data.cID,c.httpClients[cID].onEvent(g.data.eventName,g.data.data))};this.sock.onclose=function(){console.log("close")};this.createSocket=function(c,g,j){return new Socket(this,c,g,j)};this.createHTTPClient=function(){return new Http(this)}};function WebTCPIO(){this.onEvent=function(j,g){this["on"+j](g);if(this["on"+j+"Custom"])this["on"+j+"Custom"](g)};this.on=function(j,g){this["on"+j+"Custom"]=g};this.rpc=function(j,g){var c=this.createPacket();c.command=j;c.args=g;this.send(JSON.stringify(c));return c.id};this.write=function(j){var g=this.createPacket();g.data=j;this.send(JSON.stringify(g));return g.id};this.send=function(j){if(this.webtcp.ready)this.webtcp.sock.send(j);else{if(this.webtcp.outputBuffer>this.webtcp.BUFF_SIZE)throw"Output buffer is already full, but sockJS connection is not ready yet"; +this.webtcp.outputBuffer.push(j)}};this.onerror=function(j){throw j;}};
\ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java index a3b66e09..2fe4e172 100644 --- a/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -206,7 +206,7 @@ public class IterPeeps { * True if Node is on the WorkList */ public boolean on( E x ) { return _on.get(x._nid); } - boolean isEmpty() { return _len==0; } + public boolean isEmpty() { return _len==0; } /** * Removes a random Node from the WorkList; null if WorkList is empty diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index 81c7be32..56233250 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -228,7 +228,7 @@ public class Parser { RegionNode r = new RegionNode( null,null,null).init(); assert r.inProgress(); PhiNode rmem = new BulkMemPhiNode(ScopeNode.MEM0,r,null).init(); - PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init(); + PhiNode rrez = new PhiNode(ScopeNode.ARG0,r,null).init(); ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init(); fun.setRet(ret); assert ret.inProgress(); @@ -617,7 +617,7 @@ public class Parser { // Merge results RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); - Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type).glb(false),r,lhs.unkeep(),rhs.unkeep())); + Node ret = peep(new PhiNode("",r,lhs.unkeep(),rhs.unkeep())); // Immediately fail e.g. `arg ? 7 : ptr` ParseException err; if( !fside.equals("else") && (err=ret.err()) !=null ) throw err; @@ -664,7 +664,8 @@ public class Parser { // Else safety check for accessing fields out of function scope. if( instance(def) ) { pos(old_pre_equals); // setup for `self.name = expr` - Node self = _scope.in(_scope.lookup("self")); // Insert self reference + Var selfVar = _scope.lookup("self"); + Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); return parsePostfixName(self,name); } @@ -690,33 +691,16 @@ public class Parser { // Apply declared access permissions; widen floats and narrow integers. // Early error if types do not match variable. - private Node liftExpr( Node expr, Type t, boolean isLoad ) { - if( expr._type instanceof TypeMemPtr tmp && TYPES.get(tmp._obj._name).isFRef() ) - throw error("Must define forward ref "+tmp._obj._name); - // Read-only access is deep; a fixed binding alone does not remove access. - if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) { + private Node liftExpr(Node expr, Type t, boolean isLoad) { + if( t==Type.BOTTOM || t==Type.TOP ) return expr; + // Constructor sentinels record missing initialization, not a value + // needing conversion. Keep them visible to the completion check. + if( expr instanceof ConstantNode && !(expr instanceof FRefNode) && + (expr._type==Type.TOP || expr._type==Type.BOTTOM) ) return expr; + if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) expr = peep(new ReadOnlyNode(expr)); - } - if( !expr._type.accessISA(t) ) - throw error("Type " + expr._type.str() + " is not of declared type " + t.str()); - // Auto-widen array to i64 (cast ptr to raw int bits) - if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) - expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),off(tmp._obj,"[]"))); - // Auto-widen int to float - expr = widenInt( expr, t ); - // Auto-narrow wide ints to narrow ints. For loads, emit code to force - // the loaded value to match the declared sign/zero bits. For stores, - // just force the type, acting "as if" the store silently truncates. - if( expr._type instanceof TypeFloat && t instanceof TypeInteger ) - expr = peep(new ToIntegerNode(expr)); - Type et = expr._type; - if( isLoad ) { expr = zsMask(expr,t); et = expr._type; } - else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t; - - // Type is sane - if( et!=Type.BOTTOM && (!et.shallowISA(t) || !et.accessISA(t)) ) - expr = peep(new CastNode(t.isConstant() ? t : t.widen(),null,expr)); - return expr; + if( expr instanceof FunPtrNode && t instanceof TypeFunPtr ) return expr; + return peep(new ConvertNode(t,expr)); } private Node widenInt( Node expr, Type t ) { @@ -828,8 +812,6 @@ public class Parser { // Lift type to the declaration. This will report as an error later if // we cannot lift the type. - if( !lift._type.isa(t) ) - lift = peep(new CastNode(t.widen(),null,lift)); // Define a new name if( !_scope.define(name,t,xfinal || fld_final,lift, loc) ) throw error("Redefining name '" + name + "'", loc); @@ -1083,7 +1065,7 @@ public class Parser { else if( !match("!=") ) break; lhs.keep(); Node rhs = parseComparison(); - lhs = peep(new BoolNode.EQ(lhs.unkeep(),rhs).widen()); + lhs = peep(new BoolNode.EQ(lhs.unkeep(),rhs)); if( !eq ) lhs = peep(new NotNode(lhs)); } return lhs; @@ -1149,7 +1131,7 @@ public class Parser { ctrl(ifT.unkeep()); RegionNode r = fail.mergeScopes(_scope, loc()).init(); _scope = fail; - return new PhiNode("",TypeInteger.BOOL,r,con(0),con(1)).peephole(); + return new PhiNode("", r,con(0),con(1)).peephole(); } private int parseCompDir() { @@ -1164,7 +1146,7 @@ public class Parser { if( Math.abs(dir)==2 ) { Node tmp=lhs; lhs=rhs; rhs=tmp; } Node cmp = dir < 0 ? new BoolNode.LE(lhs,rhs) : new BoolNode.LT(lhs,rhs); - return peep(cmp.widen()); + return peep(cmp); } /** @@ -1187,7 +1169,7 @@ public class Parser { ParseException err; if( (err=lhs.err()) != null ) throw err; - lhs = peep(lhs.widen()); + lhs = peep(lhs); } return lhs; } @@ -1208,7 +1190,7 @@ public class Parser { else if( match("-") ) lhs = new SubNode(lhs,null); else break; lhs.setDef(2,parseMultiplication()); - lhs = peep(lhs.widen()); + lhs = peep(lhs); } return lhs; } @@ -1229,7 +1211,7 @@ public class Parser { else if( match("/") ) lhs = new DivNode(lhs,null); else break; lhs.setDef(2,parseUnary()); - lhs = peep(lhs.widen()); + lhs = peep(lhs); } return lhs; } @@ -1253,9 +1235,7 @@ public class Parser { if( n != null && !(n.type() instanceof TypeMemPtr) ) { if( n._final ) throw error("Cannot reassign final '"+n._name+"'"); - Node expr = n.type() instanceof TypeFloat - ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(delta)))) - : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); + Node expr = liftExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type(),true); _scope.update(n,expr); return expr; } @@ -1263,7 +1243,7 @@ public class Parser { // Reset, try again pos(old); } - if (match("-")) return peep(new MinusNode(parseUnary()).widen()); + if (match("-")) return peep(new MinusNode(parseUnary())); if (match("!")) return peep(new NotNode(parseUnary())); // Else be a primary expression return parsePrimary(); @@ -1325,13 +1305,18 @@ public class Parser { // If a method var, allow nested blocks and methods, but not fcns or other structs. // Else safety check for accessing fields out of function scope. if( instance(var) ) { - Node self = _scope.in(_scope.lookup("self")); // Insert self reference + Var selfVar = _scope.lookup("self"); + Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); return parsePostfixName(self,id); } // Load local value - Node rvalue = _scope.in(var); - if( rvalue._type == Type.BOTTOM ) + Node oldValue = _scope.in(var); + if( oldValue instanceof ConstantNode && !(oldValue instanceof FRefNode) && oldValue._type==Type.BOTTOM ) + throw error("Cannot read uninitialized field '"+id+"'"); + Node rvalue = liftExpr(oldValue,var.type(),true); + if( rvalue!=oldValue ) _scope.update(var,rvalue); + if( rvalue instanceof ConstantNode && rvalue._type == Type.BOTTOM ) if( rvalue instanceof FRefNode ) return parsePostfix(rvalue); else throw error("Cannot read uninitialized field '"+id+"'"); @@ -1407,7 +1392,7 @@ public class Parser { }; // Convert to float ops, or narrow int types; error if not declared type. // Also, if postfix LHS is still keep() - return liftExpr(peep(op.widen()),t,true); + return liftExpr(peep(op),t,true); } @@ -1458,7 +1443,7 @@ public class Parser { } // Check that all fields are initialized for( int i=idx; i<init.size(); i++ ) - if( init.at(i)._type == Type.TOP || init.at(i)._type == Type.BOTTOM ) + if( missingInit(init.at(i),new java.util.BitSet()) ) throw error("'"+tmp._obj._name+"' is not fully initialized, field '" + fs[i-idx]._fname + "' needs to be set in a constructor"); Node ptr = newStruct(tmp._obj, off(tmp._obj, " len"), idx, init ); if( hasConstructor ) @@ -1466,6 +1451,20 @@ public class Parser { return ptr; } + // The old constructor model requires each field on every path. An + // unresolved expression also has BOTTOM now, so inspect the actual + // sentinel and merges instead of mistaking an unknown type for no write. + private boolean missingInit(Node n, java.util.BitSet visit) { + if( n==null || visit.get(n._nid) || n instanceof FRefNode ) return false; + visit.set(n._nid); + if( n instanceof ConstantNode ) return n._type==Type.BOTTOM || n._type==Type.TOP; + if( n instanceof ConvertNode || n instanceof ReadOnlyNode ) return missingInit(n.in(1),visit); + if( n instanceof PhiNode phi ) + for( int i=1; i<n.nIns(); i++ ) + if( phi.region().in(i)!=null && phi.region().in(i)._type!=Type.XCONTROL && missingInit(n.in(i),visit) ) return true; + return false; + } + /** * Return a NewNode initialized memory. @@ -1581,14 +1580,15 @@ public class Parser { // Sanity check expr for being a reference if( !(expr._type instanceof TypeMemPtr ptr) ) - throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() ); + return parseUnknownField(expr,name); // Find the field from the Type. Lookup in the base object field names. TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); if( !ptr._obj.isAry() ) - ptr = ptr.makeFrom((TypeStruct)ptr._obj.join(ptr2._obj)); // Resolve the shape, retaining access. + ptr = ptr.makeFrom(ptr2._obj); // The declaration fixes the field shape; retain this view's access. TypeStruct base = ptr._obj; int fidx = base.find(name); + if( fidx==-1 && base._open ) return parseUnknownField(expr,name); if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); @@ -1620,7 +1620,7 @@ public class Parser { // Load field Node load = f._one && INITS.containsKey( base._name ) ? INITS.get(base._name).in(base.find(name)) - : new LoadNode(loc(),name, f._alias, tf, mem(), expr, off); + : new LoadNode(loc(),name, f._alias, mem(), expr, off); // Arrays include control, as a proxy for a safety range check // Structs don't need this; they only need a NPE check which is // done via the type system. @@ -1641,7 +1641,7 @@ public class Parser { // Method call: Loaded a function from a named field, AND the base // field type is final. if( load._type instanceof TypeFunPtr && name!="[]" && - !(load instanceof ExternNode) && + _code.externFunc((TypeFunPtr)load._type)==null && ((TypeMemPtr)TYPES.get(base._name))._obj.field(name)._final && // And calling a function match("(") ) { @@ -1658,6 +1658,52 @@ public class Parser { } + // Syntax fixes the access even when a forward definition or a loop + // leaves the receiver untyped. Resolve its alias and layout in the graph. + private Node parseUnknownField(Node expr, String name) { + expr.keep(); + Node off = new FldOffNode(expr,name).peephole(); + if( name.equals("[]") ) { + Node index = require(parseAsgn(),"]"); + Node scale = new FldOffNode(expr,"<<").peephole(); + off = peep(new AddNode(off,peep(new ShlNode(loc(),index,scale)))); + } + off.keep(); + if( matchOpx('=','=') ) { + Node value = parseAsgn().keep(); + Node st = new StoreNode(loc(),name,1,Type.BOTTOM,mem(),expr,off,value,false); + st.setDef(0,ctrl()); + mem(st.peephole()); + expr.unkill(); + off.unkill(); + return value.unkeep(); + } + Node load = new LoadNode(loc(),name,1,mem(),expr,off); + load.setDef(0,ctrl()); + load = peep(load); + char ch = _lexer.matchOperAssign(); + if( ch!=0 ) { + Node op = opAssign(ch,load,Type.BOTTOM).keep(); + Node st = new StoreNode(loc(),name,1,Type.BOTTOM,mem(),expr,off,op,false); + st.setDef(0,ctrl()); + mem(st.peephole()); + // The field width is not known yet. A compound assignment returns + // the stored value after conversion; postfix returns the old load. + // Reading our own Store folds once its alias and width are known. + if( postfix(ch) ) load.unkeep(); + else { + load = new LoadNode(loc(),name,1,mem(),expr,off); + load.setDef(0,ctrl()); + load = peep(load); + } + load.keep(); + op.unkill(); + } else load.keep(); + expr.unkill(); + off.unkill(); + return parsePostfix(load.unkeep()); + } + // zero/sign extend. "i" is limited to either classic unsigned (min==0) or // classic signed (min=minus-power-of-2); max=power-of-2-minus-1. private Node zsMask(Node val, Type t ) { @@ -1729,7 +1775,7 @@ public class Parser { private Node functionCall(Node fcn, Node self) { if( fcn._type == Type.NIL ) throw error("Calling a null function pointer"); - if( !(fcn instanceof FRefNode) && !fcn._type.isa(TypeFunPtr.BOT) ) + if( !(fcn instanceof FRefNode) && fcn._type!=Type.BOTTOM && fcn._type!=TypeScalar.BOT && !fcn._type.isa(TypeFunPtr.BOT) ) throw error("Expected a function but got "+fcn._type.glb(false).str()); fcn.keep(); // Keep while parsing args @@ -1915,6 +1961,10 @@ public class Parser { // Tracks current position in input buffer private int _position = 0; + public boolean before(Lexer that) { + return that!=null && _input==that._input && _position<that._position; + } + // private int _line_number = 1; // Start of current line diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index af670bda..b79eb73d 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -202,6 +202,15 @@ public class CodeGen { public CodeGen parse() { return phase(Phase.Parse, _phase==null, () -> { P.parse(); + // Close the declarations as a group before replacing old forward + // shapes in the graph. A can mention B before B mentions A; both + // must use the same complete cycle, including through functions. + HashMap<String,Type> closed = new HashMap<>(); + for( var entry : Parser.TYPES.entrySet() ) + if( entry.getValue() instanceof TypeMemPtr ptr && !ptr._obj._open ) + closed.put(entry.getKey(),ptr.makeFrom(ptr._obj.close(Parser.TYPES))); + Parser.TYPES.putAll(closed); + _start.walk(n -> { n.upgradeType(Parser.TYPES); return null; }); }); } diff --git a/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java index e8e87785..5a91cf9f 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java +++ b/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java @@ -65,7 +65,7 @@ class InstSelect { } // Produce a machine node from n; map it to flag as done so stops cycles. - map.put(n, x=_code._mach.instSelect(n) ); + map.put(n, x=select(n) ); // Stop retains the callable body. Its lifetime edge is not a machine operand. if( n instanceof FunPtrNode ) x._inputs.pop(); // Walk machine op and replace inputs with mapped inputs @@ -87,4 +87,25 @@ class InstSelect { } } + + private Node select(Node n) { + if( n instanceof GuardNode guard ) return new GuardMach(guard); + if( n instanceof PtrToIntNode ptr ) return new PtrToIntMach(ptr); + Node op = n; + if( n instanceof ModeNode mode && mode.mode()==2 ) { + // Adapt to the existing CPU selectors. As with machine expansion, + // copy inputs without adding uses to the old graph; _instOuts + // rebuilds reverse edges after selection has finished. + op = switch(n) { + case AddNode add -> new AddFNode(add); + case SubNode sub -> new SubFNode(sub); + case MulNode mul -> new MulFNode(mul); + case DivNode div -> new DivFNode(div); + case MinusNode neg -> new MinusFNode(neg); + default -> n; + }; + op._type = n._type; + } + return _code._mach.instSelect(op); + } } diff --git a/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/src/main/java/com/seaofnodes/simple/codegen/Machine.java index a95faefc..7ca465ba 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ b/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -99,8 +99,9 @@ Pre-alloc Post-Alloc SP+48 RPC // Simple functions already return canonical full-width integer values. protected static TypeInteger cReturn(ProjNode prj) { if( prj._idx==2 && prj.in(0) instanceof CallEndNode cend && - cend.call().fptr() instanceof ExternNode ext && - ((TypeFunPtr)ext._con).ret() instanceof TypeInteger ret && ret.log_size()<3 ) + cend.call().fptr()._type instanceof TypeFunPtr tfp && + CodeGen.CODE.externFunc(tfp)!=null && + tfp.ret() instanceof TypeInteger ret && ret.log_size()<3 ) return ret; return null; } diff --git a/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/src/main/java/com/seaofnodes/simple/codegen/Opto.java index 9f07c1d1..c78e732e 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/Opto.java +++ b/src/main/java/com/seaofnodes/simple/codegen/Opto.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; +import java.util.BitSet; // Optimistic Algorithm abstract public class Opto { @@ -50,6 +51,8 @@ abstract public class Opto { // OPTIMISTIC INTERPROCEDURAL SCCP sccp(code, oldTypes); + resolveNumericModes(code); + if( !code._iter._work.isEmpty() ) sccp(code, oldTypes); // After SCCP add everything changed to the worklist and see if // peepholes can make more progress. @@ -179,4 +182,63 @@ abstract public class Opto { }); } + // Resolve arithmetic modes which are ambiguous only because they belong to + // a recursive component. Float wins because integer operands can be + // converted to float. + private static void resolveNumericModes(CodeGen code) { + final BitSet visited = new BitSet(); + code._stop.walk(n -> { + if( n instanceof ModeNode modeNode && modeNode.mode()==0 ) { + visited.clear(); + visited.set(n._nid); // Inspect inputs, not Bool's integer result + int mode = 0; + for( int i=1; i<n.nIns(); i++ ) + mode |= numericEvidence(n.in(i),visited); + // A closed recursive numeric component can have no remaining + // concrete evidence after SCCP, e.g. dead all-integer loops. + // With no float evidence, settle to integer. + if( mode == 0 ) + mode = 1; + if( mode != 0 ) { + // If both evidence, FP wins + modeNode.setMode((byte)((mode&2)==2 ? 2 : 1)); + code.add(n); + code.addAll(n.outs()); + } + } + return null; + }); + } + + // Gather numeric evidence through Phi and interprocedural return cycles. + // The visited set makes self- and mutually-recursive calls finite. + private static int numericEvidence(Node n, BitSet visited) { + if( n == null ) return 0; + if( visited.get(n._nid) ) return 0; + visited.set(n._nid); + + Type t = n._type; + if( t instanceof TypeFloat ) return 2; + if( t instanceof TypeInteger ) return 1; + if( t!=Type.BOTTOM && !(t instanceof TypeTuple) ) + return 0; // Something unrelated, like a ptr + + int evidence = 0; + assert !(n instanceof ConvertNode); // No test case + switch( n ) { + case ModeNode modeNode when modeNode.mode() != 0 -> { return modeNode.mode(); } + case ModeNode modeNode -> {} + case PhiNode phi -> {} + case ReturnNode ret -> {} + case ProjNode proj when proj.in(0) instanceof CallEndNode cend -> { + for( int i=1; i<cend.nIns(); i++ ) + evidence |= numericEvidence(cend.in(i),visited); + } + default -> { return 0; } + } + for( int i=1; i<n.nIns(); i++ ) + evidence |= numericEvidence(n.in(i),visited); + + return evidence; + } } diff --git a/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java index 0997fd2f..bc698e19 100644 --- a/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java +++ b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java @@ -1,15 +1,39 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.Ary; abstract class TypeCheck { static void run(CodeGen code) { - Parser.ParseException err = code._stop.walk(n -> { - Parser.ParseException msg = n.err(); - if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage()); - return msg; + Ary<Node> errs = new Ary<>(Node.class); + code._stop.walk(n -> { + if( n.err()!=null ) errs.add(n); + return null; }); - if( err != null ) - throw err; + if( errs.isEmpty() ) return; + + // A bad Load now produces BOTTOM. Report the bad access before a + // Convert or arithmetic use that merely received its failed result. + Node first = errs.at(0); + for( Node n : errs ) + if( noBottomInputs(n) ) { first=n; break; } + for( Node n : errs ) + if( noBottomInputs(n) && + ((n instanceof MemOpNode nm && first instanceof MemOpNode fm && + nm._loc!=null && nm._loc.before(fm._loc)) || + (n instanceof CFGNode nc && first instanceof CFGNode fc && nc.idepth()<fc.idepth())) ) + first=n; + Parser.ParseException err = first.err(); + if( code._obs!=null ) code._obs.error(first,err.getMessage()); + throw err; + } + + private static boolean noBottomInputs(Node n) { + for( Node def : n._inputs ) + if( def!=null && !(def instanceof FldOffNode) && def._type==Type.BOTTOM ) + return false; + return true; } } diff --git a/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/src/main/java/com/seaofnodes/simple/node/AddFNode.java index 4ad02dbb..0547e29c 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddFNode.java @@ -7,6 +7,7 @@ import java.util.BitSet; public class AddFNode extends ArithFNode { public AddFNode(Node lhs, Node rhs) { super(lhs, rhs); } + public AddFNode(ArithNode n) { super(n); } @Override public String label() { return "AddF"; } diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java index 2bbe1053..aa0ee024 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -1,16 +1,18 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; -import static com.seaofnodes.simple.Parser.con; - public class AddNode extends ArithNode { + @Override boolean allowFloat() { return true; } public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public AddNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } @Override public String label() { return "Add"; } @Override public String op() { return "+"; } - @Override long doOp( long x, long y ) { return x + y; } + @Override long doOp( long x, long y ) { return x + y; } + @Override double doOp(double x, double y ) { return x + y; } @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { // Fold ranges like {0-1} + {2-3} into {2-4}. if( !overflow(x._min,y._min) && @@ -21,6 +23,11 @@ public class AddNode extends ArithNode { @Override public Node idealize () { + // Settle the one-shot arithmetic mode before applying family-specific + // algebraic identities. + if( _mode==0 ) + return super.idealize(); + Node lhs = in(1); Node rhs = in(2); if( rhs instanceof AddNode add && add.err()!=null ) @@ -29,12 +36,12 @@ public class AddNode extends ArithNode { // Add of 0. We do not check for (0+x) because this will already // canonicalize to (x+0) - if( t2 == TypeInteger.ZERO && lhs._type instanceof TypeInteger ) + if( t2 == TypeInteger.ZERO && lhs._type.isa(compute()) ) return lhs; // Add of same to a multiply by 2 - if( lhs==rhs ) - return new ShlNode(null,lhs,con(1)); + if( lhs==rhs && _mode==1 ) + return new ShlNode(null,lhs,com.seaofnodes.simple.Parser.con(1)); // Goal: a left-spine set of adds, with constants on the rhs (which then fold). @@ -44,7 +51,7 @@ public class AddNode extends ArithNode { // x+(-y) becomes x-y if( rhs instanceof MinusNode minus ) - return new SubNode(lhs,minus.in(1)); + return new SubNode(lhs,minus.in(1),_mode); // Now we might see (add add non) or (add non non) or (add add add) but never (add non add) @@ -52,18 +59,21 @@ public class AddNode extends ArithNode { // Swap to (x + y) + z // Rotate (add add add) to remove the add on RHS if( rhs instanceof AddNode add ) - return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2)); + return copy(copy(lhs,add.in(1)).peephole(), add.in(2)); // Now we might see (add add non) or (add non non) but never (add non add) nor (add add add) - if( !(lhs instanceof AddNode) ) + if( !(lhs instanceof AddNode) ) { // Rotate; look for (add (phi cons) con/(phi cons)) - return spine_cmp(lhs,rhs,this) ? swap12() : phiCon(this,true); + if( spine_cmp(lhs,rhs,this) ) return swap12(); + Node n = phiCon(this,true); + return n == null ? super.idealize() : n; + } // Now we only see (add add non) // Dead data cycle; comes about from dead infinite loops. Do nothing, // the loop will peep as dead after a bit. - if( lhs.in(1) == lhs ) + if( lhs.nIns()>=2 && lhs.in(1) == lhs ) return null; // Do we have (x + con1) + con2? @@ -73,7 +83,7 @@ public class AddNode extends ArithNode { // because if it later became a constant then we could make this // transformation. if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() ) - return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole()); + return copy(lhs.in(1),copy(lhs.in(2),rhs).peephole()); // Do we have ((x + (phi cons)) + con) ? @@ -87,13 +97,13 @@ public class AddNode extends ArithNode { // Do we rotate (x + y) + z // into (x + z) + y ? if( spine_cmp(lhs.in(2),rhs,this) ) - return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2)); + return copy(copy(lhs.in(1),rhs).peephole(),lhs.in(2)); return super.idealize(); } // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor. - // Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)) ? + // Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)|(op y con)) ? // Push constant up through the phi: x + (phi con0+con0 con1+con1...) static Node phiCon(Node op, boolean rotate) { Node lhs = op.in(1); @@ -126,7 +136,7 @@ public class AddNode extends ArithNode { for( int i=1; i<ns.length; i++ ) ns[i] = op.copy(lphi.in(i), rhs instanceof PhiNode ? rhs.in(i) : rhs).peephole(); String label = lphi._label + (rhs instanceof PhiNode rphi ? rphi._label : ""); - Node phi = new PhiNode(label,lphi._minType.meet(op._type),ns).peephole(); + Node phi = new PhiNode(label,ns).peephole(); // Rotate needs another op, otherwise just the phi return lhs==lphi ? phi : op.copy(lhs.in(1),phi); } @@ -164,6 +174,5 @@ public class AddNode extends ArithNode { return lo._nid > hi._nid; } - @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); } - @Override Node copyF() { return new AddFNode(null,null); } + @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs,_mode); } } diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java index 9957472b..2bb289e9 100644 --- a/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -5,9 +5,8 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class AndNode extends ArithNode { - public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } - @Override public String label() { return "And"; } @Override public String op() { return "&"; } @Override long doOp( long x, long y ) { return x & y; } @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { @@ -20,6 +19,7 @@ public class AndNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) ) @@ -28,7 +28,7 @@ public class AndNode extends ArithNode { // And of -1. We do not check for (-1&x) because this will already // canonicalize to (x&-1). We do not check for zero, because // the compute() call will return a zero already. - if( t2i.isConstant() && t2i.value()==-1 ) + if( t2i.isConstant() && t2i.value()==-1 && forceInt(lhs) ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/src/main/java/com/seaofnodes/simple/node/ArithFNode.java b/src/main/java/com/seaofnodes/simple/node/ArithFNode.java index dafba626..7d219215 100644 --- a/src/main/java/com/seaofnodes/simple/node/ArithFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ArithFNode.java @@ -7,6 +7,7 @@ import com.seaofnodes.simple.type.TypeFloat; public abstract class ArithFNode extends Node { public ArithFNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + protected ArithFNode(ArithNode n) { super(n); } @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.open().n(in(1)).p(glabel()).n(in(2)).close(); diff --git a/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/src/main/java/com/seaofnodes/simple/node/ArithNode.java index 54290330..5f0862b2 100644 --- a/src/main/java/com/seaofnodes/simple/node/ArithNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ArithNode.java @@ -3,15 +3,22 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; +import java.util.HashMap; +import java.util.IdentityHashMap; -public abstract class ArithNode extends Node { +public abstract class ArithNode extends Node implements ModeNode { // Source location for late reported errors Parser.Lexer _loc; + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; } + public ArithNode( Parser.Lexer loc, Node lhs, Node rhs, byte mode) { this(loc,lhs,rhs); _mode=mode; } + abstract String op(); @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { @@ -20,20 +27,107 @@ public abstract class ArithNode extends Node { abstract long doOp(long x, long y); abstract TypeInteger doOp(TypeInteger x, TypeInteger y); + double doOp(double x, double y) { throw Utils.TODO(); } + + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } + static byte mode( Type t1, Type t2 ) { + if( t1 instanceof TypeFloat || t2 instanceof TypeFloat ) return 2; + if( t1 instanceof TypeInteger && t2 instanceof TypeInteger ) return 1; + if( t1 instanceof TypeMemPtr && t2 instanceof TypeMemPtr ) return 1; + if( t1 instanceof TypeFunPtr && t2 instanceof TypeFunPtr ) return 1; + return 0; + } + + // Int-only ops, such as &, | and shifts, can collapse to one input. If + // that input, or the live arms of a Phi input, are still mode-polymorphic + // ops, settle them before returning the value so the replacement remains + // at least as narrow as the int-only op. + static boolean forceInt(Node n) { + if( n instanceof ModeNode mode && mode.mode()==0 ) { + mode.setMode((byte)1); + n.setType(n.compute()); + // Mode changes remove n from GVN even if the caller's remaining + // conditions prevent replacing the int-only operation by n. + CodeGen.CODE.add(n); + } + return true; + } + + // Check that a settled mode agrees with all data inputs. Takes the Node + // instead of a varargs list both to avoid allocation and to keep this ready + // to become shared mode-aware Node behavior later. + static Parser.ParseException nodeErr(Node n, Parser.Lexer loc, String op, byte mode, boolean allowFloat) { + Type bad = null; + for( int i=1; i<n.nIns(); i++ ) { + Type t = n.in(i)._type; + if( t==null ) + return null; + if( t.isHigh() ) + return null; + // A weak input is not evidence of a bad mode while the graph is + // still being built. Loop Phis commonly await their backedge. + if( CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() && t == Type.BOTTOM ) + return null; + bad = switch( t ) { + case TypeInteger ti -> mode==1 ? null : t; + case TypeMemPtr ti -> mode==1 ? null : t; + case TypeFunPtr ti -> mode==1 ? null : t; + case TypeFloat tf -> mode==2 ? null : t; + default -> t; + }; + if( bad != null ) break; + } + boolean unsupportedFloat = false; + if( !allowFloat ) + for( int i=1; i<n.nIns(); i++ ) + if( n.in(i)._type instanceof TypeFloat ) { + bad = n.in(i)._type; + unsupportedFloat = true; + break; + } + if( bad == null ) + return null; + if( unsupportedFloat || n instanceof BoolNode ) + return Parser.error("Cannot '"+op+"' "+bad,loc); + return Parser.error("Cannot '"+op+"' "+bad,loc); + } + + // Arithmetic subclasses which have a floating-point interpretation override. + boolean allowFloat() { return false; } - // Generic airthmetic op math: high returns high; low returns low; 2 + // Generic arithmetic op math: high returns high; low returns low; 2 // constants fold; only 2 non-constants call specialized math. @Override public final Type compute() { Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return Type.TOP; - if( t1 instanceof TypeInteger x && - t2 instanceof TypeInteger y ) - return x.isConstant() && y.isConstant() - ? con(x,y) - : doOp(x,y).makeWide( (byte)Math.max(x._widen,y._widen) ); - return TypeInteger.BOT; + if( t1==null || t2==null ) return Type.TOP; + if( t1.isHigh() || t2.isHigh() ) return Type.TOP; + if( t1==Type.BOTTOM || t2==Type.BOTTOM ) return Type.BOTTOM; + byte mode = _mode==0 ? mode(t1,t2) : _mode; + if( mode==0 ) + return CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() ? Type.BOTTOM : Type.TOP; + if( mode==1 ) { + if( t1 instanceof TypeInteger x && + t2 instanceof TypeInteger y ) + return x.isConstant() && y.isConstant() + ? con(x,y) + : doOp(x,y).makeWide( (byte)Math.max(x._widen,y._widen) ); + return TypeInteger.BOT; + } + // FP handling + if( t1 instanceof TypeFloat f1 && + t2 instanceof TypeFloat f2 ) { + if( f1.isConstant() && f2.isConstant() ) + return TypeFloat.constant(doOp(f1.value(),f2.value())); + return t1.meet(t2); + } + return TypeFloat.F64; } private TypeInteger con( Type t1, Type t2 ) { @@ -46,36 +140,50 @@ public abstract class ArithNode extends Node { // push-down peephole cycle because the constants all fold first. // Returns "Phi(op(con,con),op(x,y))". - // This check is slightly different from the - // Expected to be called after other ideal checks are done. @Override public Node idealize() { + Type t1 = in(1)._type, t2 = in(2)._type; + + // Can we decide int vs flt? + if( _mode==0 ) { + byte mode = mode(t1,t2); + if( mode!=0 ) return setMode(mode).init(); + } + if( _mode==2 ) { + if( t1 instanceof TypeInteger ) return copy(new ToFloatNode(in(1)).peephole(),in(2)); + if( t2 instanceof TypeInteger ) return copy(in(1),new ToFloatNode(in(2)).peephole()); + } + if( in(1) instanceof PhiNode lhs && in(2) instanceof PhiNode rhs && lhs.nIns() >= 2 && !lhs.inProgress() && - lhs.region()==rhs.region() && !(lhs.region() instanceof FunNode) && - lhs.nIns()>2 && // A 1-input Phi will collapse already // Disallow with self-looping phi; these will collapse - (lhs.in(2)!=lhs && rhs.in(2)!=rhs) ) { - // Profit check: only 1 instance of `this` will remain, all the - // others will fold to constants. - int cnt=0; - for( int i=1; i<lhs.nIns(); i++ ) - if( isCon(lhs,rhs,i) ) - cnt++; - if( lhs.nIns()-1 - cnt <= 1 ) { - // Profit! Push up-phi and fold - Node[] ns = new Node[lhs.nIns()]; - ns[0] = lhs.in(0); - for( int i=1; i<lhs.nIns(); i++ ) { - ns[i] = isCon(lhs,rhs,i) - ? new ConstantNode(con(lhs.in(i)._type, rhs.in(i)._type)).peephole() - : copy(lhs.in(i), rhs.in(i)).peephole(); + (lhs.nIns() > 2 && lhs.in(2)!=lhs && rhs.nIns() > 2 && rhs.in(2)!=rhs) ) { + Node r = lhs.region(); + if( r==rhs.region() && !(r instanceof FunNode) && + // For loops, the backedge *must* fold away, lest we unroll forever + (!(r instanceof LoopNode) || isCon(lhs,rhs,2)) ) { + // Profit check: only 1 instance of `this` will remain, all the + // others will fold to constants. For loops, *all* must fold away + int cnt=0; + for( int i=1; i<lhs.nIns(); i++ ) + if( isCon(lhs,rhs,i) ) + cnt++; + if( lhs.nIns()-1 - cnt <= 1 ) { + // Profit! Push up-phi and fold + Node[] ns = new Node[lhs.nIns()]; + ns[0] = lhs.in(0); + for( int i=1; i<lhs.nIns(); i++ ) { + ns[i] = isCon(lhs,rhs,i) + ? ConstantNode.make(con(lhs.in(i)._type, rhs.in(i)._type)).peephole() + : copy(lhs.in(i), rhs.in(i)).peephole(); + } + String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label; + return new PhiNode(label, ns).peephole(); } - String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label; - return new PhiNode(label,lhs._minType,ns).peephole(); } } + return null; } @@ -85,14 +193,14 @@ public abstract class ArithNode extends Node { static boolean overflow( long x, long y ) { - if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow + if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow } @Override public Parser.ParseException err() { - if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null; - if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc); - if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc); - return null; + return nodeErr(this,_loc,op(),_mode,allowFloat()); } + + @Override public boolean eq( Node n ) { return _mode==((ArithNode)n)._mode; } + @Override public int hash() { return _mode; } } diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java index 0d82c5b9..02421e43 100644 --- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -3,20 +3,23 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Utils; +import java.util.HashMap; +import java.util.IdentityHashMap; import static com.seaofnodes.simple.type.TypeInteger.*; -abstract public class BoolNode extends Node { +abstract public class BoolNode extends Node implements ModeNode { - public BoolNode(Node lhs, Node rhs) { - super(null, lhs, rhs); - } + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; - abstract public String op(); // String opcode name + public BoolNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public BoolNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs); _mode = mode; } - @Override - public String label() { return getClass().getSimpleName(); } + + abstract public String op(); // String opcode name @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.open().n(in(1)).p(op()).n(in(2)).close(); @@ -26,15 +29,25 @@ abstract public class BoolNode extends Node { public TypeInteger compute() { Type t1 = in(1)._type; Type t2 = in(2)._type; + if( t1==null || t2==null ) + return BOOL; // Exactly equals? if( t1.isHigh() || t2.isHigh() ) return (TypeInteger)BOOL.dual(); if( in(1)==in(2) ) // LT fails, both EQ and LE succeed return this instanceof LT ? FALSE : TRUE; - if( t1 instanceof TypeInteger i1 && - t2 instanceof TypeInteger i2 ) - return doOp(i1,i2); + byte mode = _mode==0 ? ArithNode.mode(t1,t2) : _mode; + if( mode==0 ) + return BOOL; + // Handle integer compares + if( mode==1 ) { + if( t1 instanceof TypeInteger i1 && + t2 instanceof TypeInteger i2 ) + return doOp(i1,i2); + return BOOL; + } + // Floating compare. Mixed inputs are converted in idealize(). if( t1 instanceof TypeFloat f1 && t2 instanceof TypeFloat f2 && f1.isConstant() && f2.isConstant() ) @@ -44,22 +57,41 @@ abstract public class BoolNode extends Node { TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); } boolean doOp(double lhs, double rhs) { throw Utils.TODO(); } - Node copyF(Node lhs, Node rhs) { return null; } - public boolean isFloat() { return false; } + public boolean isFloat() { assert _mode!=0; return _mode==2; } + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } @Override public Node idealize() { // Compare of same if( in(1)==in(2) ) - return this instanceof LT ? Parser.ZERO : new ConstantNode(TRUE); + return this instanceof LT ? com.seaofnodes.simple.Parser.ZERO : ConstantNode.make(TRUE); + // Can we decide int vs flt? + Type t1 = in(1)._type, t2 = in(2)._type; + if( t1==null || t2==null ) + return null; + if( _mode==0 ) { + byte mode = ArithNode.mode(t1,t2); + if( mode!=0 ) return setMode(mode).init(); + } + if( _mode==2 ) { + if( t1 instanceof TypeInteger ) return copy(new ToFloatNode(in(1)).peephole(),in(2)); + if( t2 instanceof TypeInteger ) return copy(in(1),new ToFloatNode(in(2)).peephole()); + } + // Equals pushes constant to the right; 5==X becomes X==5. if( this instanceof EQ ) { if( !(in(2) instanceof ConstantNode) ) { // con==noncon becomes noncon==con if( in(1) instanceof ConstantNode || in(1)._nid > in(2)._nid ) - // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high - return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1)); + // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high + return new EQ(in(2),in(1)); } // Equals X==0 becomes a !X if( (in(2)._type == ZERO || in(2)._type == Type.NIL) ) @@ -75,82 +107,96 @@ abstract public class BoolNode extends Node { return null; } + @Override public boolean eq( Node n ) { return _mode==((BoolNode)n)._mode; } + @Override public int hash() { return _mode; } + @Override public Parser.ParseException err() { + return ArithNode.nodeErr(this,null,op(),_mode,!(this instanceof ULT)); + } + public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "=="; } + public EQ(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); } + public String op() { return "=="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1==i2 && i1.isConstant() ) return TRUE; if( i1._max < i2._min || i1._min > i2._max ) return FALSE; return BOOL; } - Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } - Node copyF() { return new EQF(null,null); } + @Override boolean doOp(double lhs, double rhs) { return lhs==rhs; } + Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs,_mode); } } public static class NE extends BoolNode { public NE(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "!="; } + public NE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); } + public String op() { return "!="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1==i2 && i1.isConstant() ) return TRUE; if( i1._max < i2._min || i1._min > i2._max ) return FALSE; return BOOL; } - Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs); } - Node copyF() { throw Utils.TODO(); } + @Override boolean doOp(double lhs, double rhs) { return lhs!=rhs; } + Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs,_mode); } } public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "<" ; } + public LT(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); } + public String op() { return "<" ; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max < i2._min ) return TRUE; if( i1._min >= i2._max ) return FALSE; return BOOL; } - Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } - Node copyF() { return new LTF(null,null); } + @Override boolean doOp(double lhs, double rhs) { return lhs<rhs; } + Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs,_mode); } } public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "<="; } + public LE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); } + public String op() { return "<="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max <= i2._min ) return TRUE; if( i1._min > i2._max ) return FALSE; return BOOL; } - Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } - Node copyF() { return new LEF(null,null); } + @Override boolean doOp(double lhs, double rhs) { return lhs<=rhs; } + Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs,_mode); } } + // Unsigned less that, for range checks. Not directly user writable. + public static class ULT extends BoolNode { + public ULT(Node lhs, Node rhs) { super(lhs,rhs,(byte)1); } + public ULT(Node lhs, Node rhs, byte mode) { + super(lhs,rhs,(byte)1); + assert mode==1; + } + public String op() { return "u<" ; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; + if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; + return BOOL; + } + Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } + } public static class EQF extends EQ { - public EQF(Node lhs, Node rhs) { super(lhs,rhs); } + public EQF(Node lhs, Node rhs) { super(lhs,rhs,(byte)2); } @Override boolean doOp(double lhs, double rhs) { return lhs == rhs; } @Override public boolean isFloat() { return true; } @Override Node copy(Node lhs, Node rhs) { return new EQF(lhs,rhs); } } public static class LTF extends LT { - public LTF(Node lhs, Node rhs) { super(lhs,rhs); } + public LTF(Node lhs, Node rhs) { super(lhs,rhs,(byte)2); } @Override boolean doOp(double lhs, double rhs) { return lhs < rhs; } @Override public boolean isFloat() { return true; } @Override Node copy(Node lhs, Node rhs) { return new LTF(lhs,rhs); } } public static class LEF extends LE { - public LEF(Node lhs, Node rhs) { super(lhs,rhs); } + public LEF(Node lhs, Node rhs) { super(lhs,rhs,(byte)2); } @Override boolean doOp(double lhs, double rhs) { return lhs <= rhs; } @Override public boolean isFloat() { return true; } @Override Node copy(Node lhs, Node rhs) { return new LEF(lhs,rhs); } } - // Unsigned less that, for range checks. Not directly user writable. - public static class ULT extends BoolNode { - public ULT(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "u<" ; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; - if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; - return BOOL; - } - Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } - } } diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java index 7d4f1fb8..8399ed09 100644 --- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java @@ -23,7 +23,7 @@ public class BulkMemPhiNode extends PhiNode { } private BulkMemPhiNode(String label, BitSet aliases, Node... inputs) { - super(label,TypeMem.BOT,inputs); + super(label,inputs); _aliases = aliases; } @@ -31,6 +31,7 @@ public class BulkMemPhiNode extends PhiNode { public BulkMemPhiNode(BulkMemPhiNode phi) { super(phi); _aliases = phi._aliases; } + @Override public boolean isMem() { return true; } @Override public String label() { return "BulkPhi"+_aliases; } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java index e63b3f62..b2708531 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -35,7 +35,7 @@ public class CallNode extends CFGNode { if( fptr()._type instanceof TypeFunPtr tfp && tfp._isConstant() && tfp.notNull() ) { FunNode fun = CodeGen.CODE._link(tfp); if( fun !=null ) return fun._name; - if( fptr() instanceof ExternNode ex ) return ex._extern; + return CodeGen.CODE.externFunc(tfp); } return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java index 4e93f3e6..fa7c207d 100644 --- a/src/main/java/com/seaofnodes/simple/node/CastNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java @@ -26,6 +26,14 @@ public class CastNode extends Node { @Override public String label() { return "("+_t.str()+")"; } + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + Type t = _t.upgradeType(types); + if( t==_t ) return false; + unlock(); + _t = t; + return true; + } + @Override public boolean isConst() { return true; } diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index 7c330175..8d783be7 100644 --- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -18,7 +18,7 @@ import com.seaofnodes.simple.util.SB; */ public class ConstantNode extends Node { - public final Type _con; + public Type _con; public ConstantNode( Type type ) { super(new Node[]{CodeGen.CODE._start}); _con = _type = type; @@ -56,4 +56,12 @@ public class ConstantNode extends Node { @Override public Node idealize() { return null; } @Override public boolean eq(Node n) { return _con==((ConstantNode)n)._con; } @Override int hash() { return _con.hashCode(); } + + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + Type next = _con.upgradeType(types); + if( next==_con ) return false; + unlock(); + _con=next; + return true; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/src/main/java/com/seaofnodes/simple/node/ConvertNode.java new file mode 100644 index 00000000..c5be0123 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -0,0 +1,103 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; + +import java.util.HashMap; + +// Convert a value to an authoritative destination type. The destination is +// known structurally from a declaration; the source family might sharpen later. +public class ConvertNode extends Node { + private Type _dst; + + public ConvertNode(Type dst, Node val) { + super(null,val); + // Folding this conversion would replace the Return-linked FunPtrNode + // with an unhooked ConstantNode. + assert !(dst instanceof TypeFunPtr && val instanceof FunPtrNode); + _dst = dst; + } + + public Type dst() { return _dst; } + public Node val() { return in(1); } + + @Override public String label() { return "Convert_"+_dst.str(); } + @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { + return p.p(label()).open().n(val()).close(); + } + + // The declaration fixes the result family during pessimistic parsing. + // From SCCP onward, preserve dead inputs and stronger facts in that family. + @Override public Type compute() { + Type src = val()._type; + if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() ) { + if( src.isHigh() ) return Type.TOP; + if( (src.isa(_dst) && src.accessISA(_dst)) ) return src; + } + return _dst; + } + + @Override public Node idealize() { + Type src = val()._type; + if( src==Type.BOTTOM || src==Type.TOP ) return null; + if( (src.isa(_dst) && src.accessISA(_dst)) ) return val(); + + // Managed arrays are represented by an object pointer, while C wants + // the address of the first element. The representation cast itself is + // zero-code; the Add skips the array header. + if( _dst==TypeInteger.BOT && src instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + return new AddNode(new PtrToIntNode(val()).peephole(),Parser.off(tmp._obj,"[]")); + + // Integer (and nil-as-zero) to floating point. + if( (src instanceof TypeInteger || src==Type.NIL) && _dst instanceof TypeFloat ) + return new ToFloatNode(val()); + + // Convert to i64 first, then apply the destination's integer width. + if( src instanceof TypeFloat && _dst instanceof TypeInteger ) + return new ConvertNode(_dst,new ToIntegerNode(val()).peephole()); + + // Narrow integers produce the declared sign/zero extension. + if( src instanceof TypeInteger && _dst instanceof TypeInteger dst ) { + if( dst._min==0 ) + return new AndNode(null,val(),con(dst._max)); + int shift = Long.numberOfLeadingZeros(dst._max)-1; + Node shf = con(shift); + if( shf._type==TypeInteger.ZERO ) return val(); + return new SarNode(null,new ShlNode(null,val(),shf.keep()).peephole(),shf.unkeep()); + } + + // Narrow f64 to f32. + if( src instanceof TypeFloat && _dst instanceof TypeFloat ) + return new RoundF32Node(val()); + + // Wait for more source information, or report an error after Opto. + return null; + } + + + + @Override public Parser.ParseException err() { + if( val()._type==Type.BOTTOM || + val()._type instanceof TypeNil src && src.nullable() && _dst instanceof TypeNil dst && dst.notNull() ) { + Node out = out(0); + if( out instanceof PhiNode phi ) out = phi.out(0); + if( out instanceof StoreNode st ) + return Parser.error( "'" + ((TypeMemPtr)st.ptr()._type)._obj._name + "' is not fully initialized, field '" + st._name + "' is only partially set in the constructor", null ); + } + return Parser.error("Type "+val()._type.str()+" is not of declared type "+_dst.str(),null); + } + + @Override public boolean eq(Node n) { return _dst==((ConvertNode)n)._dst; } + @Override int hash() { return _dst.hashCode(); } + + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + Type next = _dst.upgradeType(types); + if( next==_dst ) return false; + unlock(); + _dst=next; + return true; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java index 0d037a30..df1dae4e 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivFNode.java @@ -7,6 +7,7 @@ import java.util.BitSet; public class DivFNode extends ArithFNode { public DivFNode(Node lhs, Node rhs) { super(lhs, rhs); } + public DivFNode(ArithNode n) { super(n); } @Override public String label() { return "DivF"; } diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java index 7b5ae16c..d973ffa7 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java @@ -2,15 +2,18 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeFloat; import java.util.BitSet; public class DivNode extends ArithNode { + @Override boolean allowFloat() { return true; } public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public DivNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } - @Override public String label() { return "Div"; } - @Override public String op() { return "//"; } + @Override public String op() { return _mode==1 ? "//" : "/"; } - @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; } + @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; } + @Override double doOp(double x, double y ) { return x / y; } @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { return TypeInteger.BOT; } @@ -18,11 +21,19 @@ public class DivNode extends ArithNode { @Override public Node idealize() { // Div of 1. - if( in(2)._type == TypeInteger.TRUE && in(1)._type instanceof TypeInteger ) + if( in(2)._type == TypeInteger.TRUE && in(1)._type.isa(compute()) ) return in(1); + + // Div of constant + if( _mode==2 ) { + if( in(2)._type == TypeFloat.constant(1.) && in(1)._type.isa(compute()) ) + return in(1); + if( in(2)._type instanceof TypeFloat f && f.isConstant() ) + return new MulNode(in(1),ConstantNode.make(TypeFloat.constant(1.0/f.value())).peephole(),_mode); + } + return super.idealize(); } - @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); } - @Override Node copyF() { return new DivFNode(null,null); } + @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs,_mode); } } diff --git a/src/main/java/com/seaofnodes/simple/node/FldOffNode.java b/src/main/java/com/seaofnodes/simple/node/FldOffNode.java new file mode 100644 index 00000000..7605d74f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/FldOffNode.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.*; + +/** A field name whose receiver's shape is not known while parsing. */ +public class FldOffNode extends Node { + public final String _name; + public FldOffNode(Node ptr, String name) { super(null,ptr); _name=name; } + @Override public String label() { return "#"+_name; } + @Override public Type compute() { return TypeInteger.BOT; } + @Override public Node idealize() { + if( in(1)._type instanceof TypeMemPtr ptr && !ptr._obj._open ) { + if( _name.equals("<<") && ptr._obj.isAry() ) return con(ptr._obj.aryScale()); + if( ptr._obj.field(_name)!=null ) return new ConFldOffNode(ptr._obj._name,_name); + } + return null; + } + @Override public Parser.ParseException err() { + return Parser.error("Cannot resolve field '"+_name+"'",null); + } + @Override public boolean eq(Node n) { return _name.equals(((FldOffNode)n)._name); } + @Override int hash() { return _name.hashCode(); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java index 70e1c200..1ba57e85 100644 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -254,4 +254,12 @@ public class FunNode extends RegionNode { return slotRotate*8; } + + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + TypeFunPtr next = (TypeFunPtr)_sig.upgradeType(types); + if( next==_sig ) return false; + unlock(); + _sig=next; + return true; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/GuardMach.java b/src/main/java/com/seaofnodes/simple/node/GuardMach.java new file mode 100644 index 00000000..202b1ab8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/GuardMach.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; + +public class GuardMach extends GuardNode implements MachNode { + public GuardMach(GuardNode guard) { super(guard); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress() { return in(1)._type==Type.BOTTOM ? 0 : 1; } + @Override public void encoding(Encoding enc) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/GuardNode.java b/src/main/java/com/seaofnodes/simple/node/GuardNode.java new file mode 100644 index 00000000..f808c276 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/GuardNode.java @@ -0,0 +1,66 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeScalar; +import com.seaofnodes.simple.util.Utils; + +import java.util.HashMap; +import java.util.IdentityHashMap; + +// A branch-proven refinement to zero/null or non-zero/non-null. The value +// family is deliberately taken from the input when it becomes known. +public class GuardNode extends Node { + public final boolean _nonZero; + + public GuardNode(boolean nonZero, Node ctrl, Node val) { + super(ctrl,val); + _nonZero = nonZero; + } + + public GuardNode(GuardNode guard) { + super(guard); + _nonZero = guard._nonZero; + } + + + + @Override public String label() { return _nonZero ? "(!=0)" : "(==0)"; } + + @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { + return p.p(label()).n(in(1)); + } + + @Override public Type compute() { + // Guard is a pinned, path-local fact. If its control path is not + // executable, the fact is not executable either, regardless of how the + // value input sharpens later. + Type c = in(0)._type; + if( c != Type.CONTROL && c != Type.BOTTOM ) + return Type.TOP; + Type t = in(1)._type; + if( t==Type.BOTTOM || t==Type.TOP ) return t; + // The scalar envelope does not identify a value family. Preserve it + // until SCCP learns whether this is an int, float, pointer, or fptr; + // choosing generic ptr here can be weaker than a later concrete type. + if( t==TypeScalar.BOT || t==TypeScalar.TOP ) return t; + Type t2 = _nonZero ? t.nonZero() : t.makeZero(); + if( t2!=null ) t2 = t.join(t2); + return t2==null ? (_type==null ? Type.TOP : _type) : t2; // No sane answer for e.g. not-zero of a zero + } + + @Override public Node idealize() { + if( in(0)._type != Type.CONTROL && in(0)._type != Type.BOTTOM ) + return null; + if( in(1)._type==Type.BOTTOM || in(1)._type==Type.TOP ) return null; + Type t = in(1)._type; + if( _nonZero ? !t.makeZero().isa(t) : t==t.makeZero() ) return in(1); + if( _type.isHighOrConst() && in(1)._type.isa(_type) ) + return in(1); + return null; + } + + @Override public boolean eq(Node n) { return _nonZero==((GuardNode)n)._nonZero; } + @Override int hash() { return _nonZero ? 1 : 0; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java index c96993e5..3f21417e 100644 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -60,7 +60,9 @@ public class IfNode extends CFGNode implements MultiNode { // test on either the true or false branch, that side wins. if( !pred()._type.isHighOrConst() ) for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) { - if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) { + if( addDep(dom) instanceof IfNode iff && prior instanceof CProjNode prj && + (addDep(iff.pred())==pred() || + (pred() instanceof GuardNode guard && guard._nonZero && iff.pred()==guard.in(1))) ) { setDef(1,con( prj._idx==0 ? 1 : 0 )); return this; } diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java index e057ef1c..24a2f9b8 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -22,8 +22,10 @@ public class LoadNode extends MemOpNode { * @param ptr The ptr to the struct base from where we load a field * @param off The offset inside the struct base */ - public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { - super(loc, name, alias, true, glb, mem, ptr, off); + public LoadNode(Parser.Lexer loc, String name, int alias, Node mem, Node ptr, Node off) { + // The field declaration will supply storage width once the receiver + // is known. A parser guess must not bound the value of this Load. + super(loc, name, alias, true, Type.BOTTOM, mem, ptr, off); } @Override boolean canDrop(MemOpNode other, Node dep) { @@ -52,37 +54,40 @@ public class LoadNode extends MemOpNode { @Override public Type compute() { - Type tmem = mem()._type; - if( !(tmem instanceof TypeMem mem) ) - return tmem; // No memory yet? Assume TOP/BOT - assert !_declaredType.isFRef(); - // No lifting if ptr might null-check - Type tptr = ptr()._type; - if( err() != null ) - return _declaredType; - if( !(tptr instanceof TypeMemPtr tmp) ) - return tptr; // No pointer yet? Assume TOP/BOT - // Load field from object - Field f = tmp._obj.field(_name); - // No field? Open objects might yet get the field when falling; - // closed objects with missing field are an error. - if( f == null ) - return tmp.isHigh() ? Type.TOP : _declaredType; - Type t = f._t; - // Load member of constant array - if( t instanceof TypeConAry ary ) - t = ary.elem(); // TODO: if offset is known, can peek the constant - // Lift from declared type and memory input - t = t.join(MemMergeNode.contents(mem(),_alias,this)); - if( ptr()._type.isFinal() || _declaredType.isFinal() ) - t = t.makeRO(); // Deep final applied - // Pinch between declared type - t = t.join(_declaredType); - return t; + Type p = ptr()._type; + Type m = mem()._type; + if( p.isHigh() || m.isHigh() ) return TypeScalar.TOP; + if( p==Type.NIL || p==Type.BOTTOM ) return Type.BOTTOM; + if( !(p instanceof TypeMemPtr ptr) ) return TypeScalar.BOT; + if( ptr.nullable() ) return Type.BOTTOM; + Field field = ptr._obj.field(_name); + if( field==null ) return TypeScalar.BOT; + Type t = field._t; + if( t instanceof TypeConAry ary ) t=ary.elem(); + if( _alias!=1 ) t=t.join(MemMergeNode.contents(mem(),_alias,this)); + if( ptr.isFinal() ) t=t.makeRO(); + return t==Type.TOP ? TypeScalar.TOP : t==Type.BOTTOM ? TypeScalar.BOT : t; } @Override public Node idealize() { + if( ptr()._type instanceof TypeMemPtr ptr && !ptr._obj._open ) { + Field field = ptr._obj.field(_name); + // Storage width comes from the declaration, not from a constant + // or range inferred for this particular object's current value. + Type declared = Parser.TYPES.get(ptr._obj._name); + if( declared instanceof TypeMemPtr shape && shape._obj.field(_name)!=null ) + field=shape._obj.field(_name); + Type storage = field==null ? Type.BOTTOM : field._t instanceof TypeConAry ary ? ary.elem() : field._t; + if( field!=null && (_alias!=field._alias || _declaredType!=storage) ) { + unlock(); + _alias=field._alias; + _declaredType=storage; + return this; + } + } + if( _alias==1 ) return null; + if( mem() instanceof CastNode cast ) { setDef(1,cast.in(1)); return this; @@ -128,7 +133,7 @@ public class LoadNode extends MemOpNode { !(ptr instanceof PhiNode p && p.region()==memphi.region()) ? memphi : null; if( find(memphi.in(2),ptr,stop,new BitSet())!=null || (!(memphi.region() instanceof LoopNode) && find(memphi.in(1),ptr,null,new BitSet())!=null) ) { - PhiNode phi = new PhiNode(_name,_declaredType,memphi.region()).keep(); + PhiNode phi = new PhiNode(_name,memphi.region()).keep(); phi.setType(_type); // Backedge searches may return this value Phi. for( int i=1; i<memphi.nIns(); i++ ) { Node p = ptr instanceof PhiNode pp && pp.region()==memphi.region() ? pp.in(i) : ptr; @@ -153,7 +158,7 @@ public class LoadNode extends MemOpNode { // the unchanged loop memory, or a merge whose every arm folds. Null fails. // Register dependencies so a later pointer/offset rewrite retries the query. private Node find(Node mem, Node ptr, Node stop, BitSet visit) { - if( ptr instanceof ReadOnlyNode ro ) ptr=ro.in(1); + ptr = address(ptr); while( mem!=null ) { addDep(mem); if( mem==stop ) return mem; @@ -161,9 +166,12 @@ public class LoadNode extends MemOpNode { case MemMergeNode merge: mem=merge.alias(_alias); break; case CastNode cast: mem=cast.in(1); break; case StoreNode st: + // A symbolic Store may write this field. Wait for its alias; + // skipping it can make a compound assignment read the old value. + if( st._alias==1 ) return null; addDep(st.ptr()); - if( _alias==st._alias && ptr==st.ptr() && off()==st.off() ) return st; - if( _alias==st._alias && !neverAlias(ptr,st.ptr()) && + if( _alias==st._alias && ptr==address(st.ptr()) && off()==st.off() ) return st; + if( _alias==st._alias && !neverAlias(ptr,address(st.ptr())) && !addDep(off())._type.join(addDep(st.off())._type).isHigh() ) return null; mem=st.mem(); break; @@ -190,6 +198,13 @@ public class LoadNode extends MemOpNode { return null; } + // Guards and read-only views keep the same address. Retain the Load's + // guarded input for its null check, but compare actual addresses here. + private static Node address(Node ptr) { + while( ptr instanceof ReadOnlyNode || ptr instanceof GuardNode ) ptr=ptr.in(1); + return ptr; + } + private Node folded(Node win) { return win instanceof StoreNode st ? extend(castRO(st.val())) : zero((NewNode)win); } @@ -201,13 +216,13 @@ public class LoadNode extends MemOpNode { if( win!=null ) { if( win==stop ) return value; if( win instanceof MemPhiNode mp ) { - PhiNode phi=new PhiNode(_name,_declaredType,mp.region()).keep(); + PhiNode phi=new PhiNode(_name,mp.region()).keep(); for( int i=1; i<mp.nIns(); i++ ) phi.addDef(load(mp.in(i),ptr,stop,value)); return phi.unkeep().peephole(); } return folded(win).peephole(); } - LoadNode ld=new LoadNode(_loc,_name,_alias,_declaredType,mem,ptr,off()); + LoadNode ld=new LoadNode(_loc,_name,_alias,mem,ptr,off()); ld.setDef(0,in(0)); return ld.peephole(); } diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index e7b2c792..1f0fc4d3 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -22,7 +22,7 @@ import java.util.ArrayList; public abstract class MemOpNode extends Node { // The equivalence alias class - public final int _alias; + public int _alias; // True if load-like, false if store-like. // @@ -143,4 +143,12 @@ public abstract class MemOpNode extends Node { } } } + + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + Type next = _declaredType.upgradeType(types); + if( next==_declaredType ) return false; + unlock(); + _declaredType=next; + return true; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java index 787333d9..ba53cfd8 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java @@ -9,20 +9,21 @@ public class MemPhiNode extends PhiNode { public final int _alias; public MemPhiNode(String label, int alias, Node... inputs) { - super(label,TypeMem.make(alias,Type.BOTTOM),inputs); + super(label,inputs); assert alias > 1; _alias = alias; } public MemPhiNode(MemPhiNode phi) { super(phi); _alias = phi._alias; } + @Override public boolean isMem() { return true; } @Override public String label() { return "MemPhi_"+_alias; } @Override public Type compute() { assert BulkMemPhiNode.checkMem(this,_alias); if( !(region() instanceof RegionNode r) ) return (region()._type==Type.XCONTROL || region()._type==Type.TOP) ? TypeMem.TOP : _type; - if( r.inProgress() ) return r instanceof LoopNode ? _minType : TypeMem.BOT; + if( r.inProgress() ) return r instanceof LoopNode ? TypeMem.make(_alias,Type.BOTTOM) : TypeMem.BOT; Type t = Type.TOP; for( int i=1; i<nIns(); i++ ) if( addDep(r.in(i))._type!=Type.XCONTROL && r.in(i)._type!=Type.TOP ) diff --git a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java index 74439b8c..c3c27883 100644 --- a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java @@ -7,6 +7,7 @@ import com.seaofnodes.simple.type.TypeFloat; public class MinusFNode extends Node { public MinusFNode(Node in) { super(null, in); } + public MinusFNode(MinusNode n) { super(n); } @Override public String label() { return "MinusF"; } diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java index 94c69785..4d7e3ddb 100644 --- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -1,33 +1,77 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Utils; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class MinusNode extends Node implements ModeNode { + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; -public class MinusNode extends Node { public MinusNode(Node in) { super(null, in); } + public MinusNode(Node in, byte mode) { super(null, in); _mode = mode; } + - @Override public String label() { return "Minus"; } + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } @Override protected String format() { return "(-%1)"; } @Override public Type compute() { - if( in(1)._type.isHigh() ) return TypeInteger.TOP; - if( in(1)._type instanceof TypeInteger i0 && - i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE ) - return TypeInteger.make(-i0._max,-i0._min,i0._widen); - return TypeInteger.BOT; + Type t = in(1)._type; + if( t==null ) + return Type.TOP; + if( t.isHigh() ) + return _mode==0 ? Type.TOP : + _mode==1 ? TypeInteger.TOP : + t instanceof TypeFloat ? t : TypeFloat.F64.dual(); + byte mode = _mode; + if( mode==0 ) { + if( t instanceof TypeFloat ) mode=2; + if( t instanceof TypeInteger ) mode=1; + } + if( mode==0 ) + return CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() ? Type.BOTTOM : Type.TOP; + if( mode==1 ) { + if( t instanceof TypeInteger i0 && + i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE ) + return TypeInteger.make(-i0._max,-i0._min,i0._widen); + return TypeInteger.BOT; + } + if( t instanceof TypeFloat f ) + return f.isConstant() ? TypeFloat.constant(-f.value()) : f; + return TypeFloat.F64; } @Override public Node idealize() { // -(-x) is x - if( in(1) instanceof MinusNode minus && addDep(minus.in(1))._type instanceof TypeInteger ) + if( in(1) instanceof MinusNode minus && addDep(minus.in(1))._type.isa(compute()) ) return minus.in(1); + // Can we decide int vs flt? + if( _mode==0 ) { + byte mode = (byte)( + in(1)._type instanceof TypeInteger ? 1 : + in(1)._type instanceof TypeFloat ? 2 : 0); + if( mode!=0 ) return setMode(mode).init(); + } + return null; } - @Override Node copyF() { return new MinusFNode(null); } + @Override public Parser.ParseException err() { + return ArithNode.nodeErr(this,null,"-",_mode,true); + } + @Override public boolean eq( Node n ) { return _mode==((MinusNode)n)._mode; } + @Override public int hash() { return _mode; } } diff --git a/src/main/java/com/seaofnodes/simple/node/ModeNode.java b/src/main/java/com/seaofnodes/simple/node/ModeNode.java new file mode 100644 index 00000000..dc635447 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/ModeNode.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node; + +/** + * A node whose numeric input family is selected once: unknown, integer, or + * floating point. Implementors remain ordinary Nodes; this interface only + * centralizes the structural mode contract shared by arithmetic, comparisons, + * and unary minus. + */ +public interface ModeNode { + byte mode(); + Node setMode(byte mode); +} diff --git a/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/src/main/java/com/seaofnodes/simple/node/MulFNode.java index 5c04af43..1126319b 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulFNode.java @@ -7,6 +7,7 @@ import java.util.BitSet; public class MulFNode extends ArithFNode { public MulFNode(Node lhs, Node rhs) { super(lhs, rhs); } + public MulFNode(ArithNode n) { super(n); } @Override public String label() { return "MulF"; } diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java index 6e1dc053..37d6a2bc 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -1,17 +1,19 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class MulNode extends ArithNode { + @Override boolean allowFloat() { return true; } public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public MulNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } - @Override public String label() { return "Mul"; } @Override public String op() { return "*"; } - @Override long doOp( long x, long y ) { return x * y; } + @Override long doOp( long x, long y ) { return x * y; } + @Override double doOp(double x, double y ) { return x * y; } @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { return TypeInteger.BOT; } @@ -32,8 +34,8 @@ public class MulNode extends ArithNode { // Mul of 1. We do not check for (1*x) because this will already // canonicalize to (x*1) long c = i.value(); - if( c==1 ) return lhs._type instanceof TypeInteger ? lhs : null; - if( c==0 ) return Parser.ZERO; + if( c==1 ) return lhs._type.isa(compute()) ? lhs : super.idealize(); + if( c==0 ) return com.seaofnodes.simple.Parser.ZERO; // Mul by a power of 2, +/-1. Bit patterns more complex than this // are unlikely to win on an X86 vs the normal "imul", and so // become machine-specific. @@ -58,6 +60,5 @@ public class MulNode extends ArithNode { return super.idealize(); } - @Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); } - @Override Node copyF() { return new MulFNode(null,null); } + @Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs,_mode); } } diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java index 840d2759..bdcdfdfc 100644 --- a/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -14,7 +14,7 @@ import com.seaofnodes.simple.util.Utils; */ public class NewNode extends Node implements MultiNode { - public final TypeMemPtr _ptr; + public TypeMemPtr _ptr; public NewNode(TypeMemPtr ptr, Node... nodes) { super(nodes); @@ -29,6 +29,14 @@ public class NewNode extends Node implements MultiNode { public Node mem() { return in(1); } public Node size() { return in(2); } + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + TypeMemPtr ptr = (TypeMemPtr)_ptr.upgradeType(types); + if( ptr==_ptr ) return false; + unlock(); + _ptr = ptr; + return true; + } + public Field field(int alias) { for( Field f : _ptr._obj._fields ) if( !f._one && f._alias==alias ) return f; diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index 414530b4..230593a8 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -71,7 +71,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable { } // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" - public abstract String label(); + public String label() { return getClass().getSimpleName().replace("Node",""); } public String p(int depth) { return IRPrinter.prettyPrint(this,depth); } @@ -690,4 +690,16 @@ public abstract class Node extends BaseNode<Node> implements Cloneable { for( Node use : _outputs ) if( use != null && (x = use._find(nid,bs)) != null ) return x; return null; } + + boolean _upgradeType(java.util.HashMap<String,Type> types) { return false; } + public final void upgradeType(java.util.HashMap<String,Type> types) { + Type old = _type; + _type = _type.upgradeType(types); + boolean progress = _upgradeType(types); + if( progress || old!=_type ) { + CODE.add(this); + CODE.addAll(_outputs); + moveDepsToWorklist(); + } + } } diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java index d16dd194..618c5340 100644 --- a/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -5,7 +5,7 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class OrNode extends ArithNode { - public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public OrNode( Parser.Lexer loc, Node lhs, Node rhs) { super(loc,lhs, rhs, (byte)1); } @Override public String label() { return "Or"; } @Override public String op() { return "|"; } @@ -18,12 +18,13 @@ public class OrNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; // Or of 0. We do not check for (0|x) because this will already // canonicalize to (x|0) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && lhs._type instanceof TypeInteger ) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && t1.isa(TypeInteger.BOT) && forceInt(lhs) ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java index cc654abb..52fe5704 100644 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -10,13 +10,15 @@ import com.seaofnodes.simple.type.TypeMemPtr; public class ParmNode extends PhiNode { // Argument indices are mapped one-to-one on CallNode inputs + private Type _declaredType; public final int _idx; // Argument index public ParmNode(String label, int idx, Type declaredType, Node... inputs) { - super(label,declaredType,inputs); + super(label,inputs); _idx = idx; + _declaredType = declaredType; } - public ParmNode(ParmNode parm) { super(parm, parm._label, parm._minType ); _idx = parm._idx; } + public ParmNode(ParmNode parm) { super(parm, parm._label, parm._declaredType ); _idx = parm._idx; _declaredType = parm._declaredType; } @Override public String label() { return MemOpNode.mlabel(_label); } @@ -37,7 +39,7 @@ public class ParmNode extends PhiNode { @Override public Type compute() { - Type t = super.compute(); + Type t = inProgress() ? _declaredType : super.compute().join(_declaredType); // Recursion is a loop in disguise. A Parm merges the first argument // with those from recursive calls, just as a loop Phi merges its entry // and backedge. Use the widening rule in PhiNode.compute here too. @@ -46,7 +48,7 @@ public class ParmNode extends PhiNode { if( t instanceof TypeInteger ti && t != _type && !ti.isHigh() && !ti.isConstant() && (!(_type instanceof TypeInteger oldi) || ti._widen <= oldi._widen) ) - return ti.same_but_slightly_wider_than(_minType); + return ti.same_but_slightly_wider_than(_declaredType); return t; } @@ -58,10 +60,10 @@ public class ParmNode extends PhiNode { if( fun()._folding ) return super.idealize(); // Can upgrade minType even while in-progress - if( _minType instanceof TypeMemPtr tmp && _minType.isFRef() ) { + if( _declaredType instanceof TypeMemPtr tmp && _declaredType.isFRef() ) { TypeMemPtr tmp2 = (TypeMemPtr) Parser.TYPES.get(tmp._obj._name); - if( tmp2!=null && tmp2 != _minType ) { - _minType = tmp2; + if( tmp2!=null && tmp2 != _declaredType ) { + _declaredType = tmp2; return this; } } @@ -72,4 +74,12 @@ public class ParmNode extends PhiNode { @Override public boolean eq( Node n ) { return ((ParmNode)n)._idx==_idx && super.eq(n); } + + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + Type next = _declaredType.upgradeType(types); + if( next==_declaredType ) return false; + unlock(); + _declaredType=next; + return true; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 7f2bb1f4..064852e4 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -11,33 +11,27 @@ public class PhiNode extends Node { public final String _label; - // The Phi type we compute must stay within the domain of the Phi. Example - // Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem. - Type _minType; - int lattice_drop; - public PhiNode(String label, Type minType, Node... inputs) { + public PhiNode(String label, Node... inputs) { super(inputs); _label = label; - assert minType!=null; - _minType = minType; } // Used by ParmNode - public PhiNode(PhiNode phi, String label, Type minType) { super(phi); _label = label; _type = _minType = minType; } + public PhiNode(PhiNode phi, String label, Type type) { super(phi); _label = label; _type = type; } // Used by instruction Selection - public PhiNode(PhiNode phi) { this(phi,phi._label,phi._minType ); } + public PhiNode(PhiNode phi) { this(phi,phi._label,phi._type ); } // Used by the infinite-loop exit breaker public PhiNode(RegionNode r, Node sample) { super(new Node[]{r}); _label = ""; - _minType = sample._type; + _type = sample._type; while( nIns() < r.nIns() ) addDef(sample); } public static PhiNode make(String label, Type type, Node... inputs) { - return type instanceof TypeMem ? new BulkMemPhiNode(label,inputs) : new PhiNode(label,type,inputs); + return type instanceof TypeMem ? new BulkMemPhiNode(label,inputs) : new PhiNode(label,inputs); } @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); } @@ -52,7 +46,7 @@ public class PhiNode extends Node { } public CFGNode region() { return (CFGNode)in(0); } - @Override public boolean isMem() { return _minType instanceof TypeMem; } + @Override public boolean isMem() { return _type instanceof TypeMem; } boolean isRPC() { return false; } @Override @@ -61,11 +55,9 @@ public class PhiNode extends Node { return region()._type==Type.XCONTROL || region()._type==Type.TOP ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type; // During parsing Phis have to be computed type pessimistically. if( r.inProgress() || in(nIns()-1)==null ) - // Loop-Phis must lift to the declared type, because that is how - // the Parser keeps precise types until the loop finishes parsing. - // Similar, ParmNodes use precise minType until all calls are - // linked (post opto). - return r instanceof LoopNode || (this instanceof ParmNode) ? _minType : Type.BOTTOM; + // The backedge has not been parsed. Declarations belong on + // Convert/Parm nodes; an unfinished value Phi knows no bound. + return Type.BOTTOM; // Set type to local top of the starting type Type t = Type.TOP; for (int i = 1; i < nIns(); i++) { @@ -78,7 +70,7 @@ public class PhiNode extends Node { t = t.meet(in(i)._type); } } - Type newt = t.join( _minType ); + Type newt = t; // Widen loop Phis so integer ranges cannot grow one step per trip forever. // Recursion is a loop in disguise: ParmNode.compute widens arguments, @@ -91,7 +83,7 @@ public class PhiNode extends Node { // Types changed and are falling (the optimistic case, expected to fall forever) newi != _type ) { if( !newi.isHigh() && !newi.isConstant() && (!(_type instanceof TypeInteger oldi) || newi._widen <= oldi._widen) ) - return newi.same_but_slightly_wider_than(_minType); + return newi.same_but_slightly_wider_than(TypeInteger.BOT); } return newt; @@ -101,27 +93,12 @@ public class PhiNode extends Node { public Node idealize() { if( !(region() instanceof RegionNode r ) ) return in(1); // Input has collapse to e.g. starting control. - // Can upgrade minType even while in-progress - if( _minType instanceof TypeMemPtr tmp && _minType.isFRef() ) { - TypeMemPtr tmp2 = (TypeMemPtr) Parser.TYPES.get(tmp._obj._name); - if( tmp2!=null && tmp2._obj != tmp._obj ) { - _minType = tmp.makeFrom(tmp2._obj); - return this; - } - } - if( r.inProgress() || r.nIns()<=1 ) + if( r.inProgress() || inProgress() || r.nIns()<=1 ) return null; // Input is in-progress // If we have only a single unique input, become it. Node live = singleUniqueInput(); - if( live != null ) { - if( live._type.isa(_type) ) - return live; - // Let the bulk input split this alias before collapsing a precise Phi. - if( live instanceof BulkMemPhiNode ) return null; - // Keep the Phi upcast - return new CastNode(_type,null,live); - } + if( live != null ) return live; // No bother if region is going to fold dead paths soon for( int i=1; i<nIns(); i++ ) @@ -132,6 +109,13 @@ public class PhiNode extends Node { Node down; if( same_op() && (down=drop_same_op())!=null ) return down; + if( nIns()==3 ) { + Node merge = matchingGuardMerge(r,1); + if( merge!=null ) return merge; + merge = matchingGuardMerge(r,2); + if( merge!=null ) return merge; + } + // If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove. if( nIns()==3 ) { if( in(1) instanceof CastNode cast && addDep(cast.in(1))==in(2) ) return in(2); @@ -162,6 +146,25 @@ public class PhiNode extends Node { return null; } + private Node matchingGuardMerge(RegionNode r, int nzIdx) { + Node zero = in(3-nzIdx); + if( !(in(nzIdx) instanceof GuardNode guard && guard._nonZero) || + zero==null || zero._type.makeZero()!=zero._type || guard.in(1)==this ) + return null; + if( !(r.in(nzIdx) instanceof CProjNode nz && nz._idx==0 && nz.ctrl() instanceof IfNode iff) ) + return null; + if( !(r.in(3-nzIdx) instanceof CProjNode z && z._idx==1 && z.ctrl()==iff) ) + return null; + Node n=guard.in(1), tested=null; + while( true ) { + if( iff.pred()==n ) tested=n; + if( !(n instanceof GuardNode g && g._nonZero) ) break; + n=g.in(1); + } + // The source must exist before the join, not just on its guarded arm. + return tested!=null && ScopeNode.availableAt(n,r,new java.util.BitSet()) ? n : null; + } + // Same op on all Phi paths; all ops have only the Phi as a use. // None have a control input. private boolean same_op() { @@ -274,21 +277,13 @@ public class PhiNode extends Node { // Never equal if inProgress. // Also, joins - @Override public boolean eq( Node n ) { - if( inProgress() ) return false; - Type min = ((PhiNode)n)._minType; - if( _minType==min ) return true; - Type mt = min.meet(_minType); - if( min!=mt && _minType!=mt ) return false; - //// Theory says these 2 Phis CAN be merged/GVNd, but I need to pick the - //// most general minType. - //_minType = ((PhiNode)n)._minType = mt; - //return true; - return false; - } + @Override public boolean eq(Node n) { return !inProgress(); } @Override public Parser.ParseException err() { + if( inProgress() ) return null; + // A scalar union is useful for truth tests, e.g. !(ptr || integer). + // A typed consumer reports an invalid use of that union itself. if( _type != Type.BOTTOM ) return null; // BOTTOM means we mixed e.g. int and ptr diff --git a/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java b/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java new file mode 100644 index 00000000..9a9b94fd --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.util.SB; + +/** Zero-code machine form of {@link PtrToIntNode}. */ +public class PtrToIntMach extends PtrToIntNode implements MachNode { + public PtrToIntMach(PtrToIntNode ptr) { super(ptr); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress() { return 1; } + @Override public void encoding(Encoding enc) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java b/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java new file mode 100644 index 00000000..55c0fc02 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node; + + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMemPtr; + + +/** + * Reinterpret a non-null managed pointer as its raw integer address. + * + * This node changes representation families but emits no code. No + * control-dependent proof can turn a pointer into an integer; array-to-C-pointer + * conversion adds the array body offset separately. + */ +public class PtrToIntNode extends Node { + public PtrToIntNode(Node ptr) { super(null,ptr); } + public PtrToIntNode(PtrToIntNode ptr) { super(ptr); } + + @Override protected String format() { return "(i64*)%1"; } + + @Override public Type compute() { + return in(1)._type.isHigh() ? TypeInteger.TOP : TypeInteger.BOT; + } + + @Override public Node idealize() { return null; } + + @Override public Parser.ParseException err() { + Type t = in(1)._type; + if( t instanceof TypeMemPtr ptr ) + return ptr.notNull() ? null : Parser.error("Cannot convert a nullable pointer to an integer",null); + return Parser.error("Cannot convert "+t.str()+" to an integer pointer",null); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index 266657be..8fea803d 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -91,8 +91,8 @@ public class ReturnNode extends CFGNode { // Merge path into the One True Return RegionNode r = (RegionNode)ctrl(); // Assert that the Phis are in particular outputs; not reordered or shuffled - PhiNode mem = (PhiNode)r.out(0); assert mem._minType == TypeMem.BOT; - PhiNode rez = (PhiNode)r.out(1); assert rez._minType == Type.BOTTOM; + PhiNode mem = (PhiNode)r.out(0); assert mem.isMem(); + PhiNode rez = (PhiNode)r.out(1); assert !rez.isMem(); // Pop "inProgress" null off r ._inputs.pop(); mem._inputs.pop(); @@ -109,7 +109,26 @@ public class ReturnNode extends CFGNode { @Override public Parser.ParseException err() { if( ctrl()._type != Type.CONTROL ) return null; // Exit path is dead - return expr()._type==Type.BOTTOM || expr()._type==Type.TOP ? mixerr(ti,tf,tp,tn,_fun._loc) : null; + if( expr()._type==TypeScalar.BOT ) { + int kinds = returnKinds(expr(),new java.util.BitSet()); + return mixerr((kinds&1)!=0,(kinds&2)!=0,(kinds&4)!=0,(kinds&8)!=0,_fun._loc); + } + return expr()._type==Type.BOTTOM || expr()._type==Type.TOP || expr()._type==TypeScalar.BOT ? mixerr(ti,tf,tp,tn,_fun._loc) : null; + } + + private static int returnKinds(Node n, java.util.BitSet visit) { + if( n==null || visit.get(n._nid) ) return 0; + visit.set(n._nid); + Type t=n._type; + if( t instanceof TypeInteger ) return 1; + if( t instanceof TypeFloat ) return 2; + if( t instanceof TypeMemPtr || t instanceof TypeFunPtr ) return 4; + if( t==Type.NIL ) return 8; + int kinds=0; + if( n instanceof PhiNode phi ) + for( int i=1; i<n.nIns(); i++ ) + if( phi.region().in(i)._type==Type.CONTROL ) kinds |= returnKinds(n.in(i),visit); + return kinds; } static Parser.ParseException mixerr( boolean ti, boolean tf, boolean tp, boolean tn, Parser.Lexer loc ) { diff --git a/src/main/java/com/seaofnodes/simple/node/SarNode.java b/src/main/java/com/seaofnodes/simple/node/SarNode.java index 429be229..54611b0d 100644 --- a/src/main/java/com/seaofnodes/simple/node/SarNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SarNode.java @@ -5,9 +5,8 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class SarNode extends ArithNode { - public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } - @Override public String label() { return "Sar"; } @Override public String op() { return ">>"; } @Override long doOp( long x, long y ) { return x >> y; } @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { @@ -22,10 +21,11 @@ public class SarNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t2 = rhs._type; // Sar of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && lhs._type instanceof TypeInteger ) + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && forceInt(lhs) && lhs._type instanceof TypeInteger ) return lhs; // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5) diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 307fc0f9..3c1e9776 100644 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -427,21 +427,14 @@ public class ScopeNode extends Node { pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole()); // This is a zero/null test. // Compute the positive test type. - Type tnz = pred._type.nonZero(); - if( tnz!=null ) - _addGuard(tnz,ctrl,pred); - - // Compute the negative test type. - if( pred instanceof NotNode not ) { - Node npred = not.in(1); - Type tzero = npred._type.makeZero(); - _addGuard(tzero,ctrl,npred); - } + _addGuard(true,ctrl,pred); + if( pred instanceof NotNode not ) + _addGuard(false,ctrl,not.in(1)); } // A Phi's value is available at its Region. Other data nodes require all // their inputs, including pinned control, to dominate the proposed guard. - private static boolean availableAt(Node n, CFGNode ctrl, BitSet visit) { + static boolean availableAt(Node n, CFGNode ctrl, BitSet visit) { if( n==null || n instanceof ConstantNode || visit.get(n._nid) ) return true; visit.set(n._nid); if( n instanceof PhiNode phi ) n = phi.region(); @@ -468,17 +461,15 @@ public class ScopeNode extends Node { return null; } - private void _addGuard(Type guard, Node ctrl, Node pred) { - Type tcast = guard.join(pred._type); - if( tcast != pred._type && !tcast.isHigh() ) { - Node cast = new CastNode(tcast,ctrl,pred.keep()).peephole().keep(); - _guards.add(pred); - _guards.add(cast); - replace(pred,cast); - } + private void _addGuard(boolean nonZero, Node ctrl, Node pred) { + Node guard = new GuardNode(nonZero,ctrl,pred.keep()).peephole(); + if( guard==pred ) { pred.unkeep(); return; } + guard.keep(); + _guards.add(pred); + _guards.add(guard); + replace(pred,guard); } - // Remove matching pred/cast pairs from this guarded region. public ScopeNode removeGuards( Node ctrl ) { assert ctrl instanceof CFGNode; diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java index e2951784..7d6e8e2e 100644 --- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -1,13 +1,13 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class ShlNode extends ArithNode { - public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } - @Override public String label() { return "Shl"; } @Override public String op() { return "<<"; } @Override long doOp( long x, long y ) { return x << y; } @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { @@ -30,13 +30,13 @@ public class ShlNode extends ArithNode { if( rhs._type instanceof TypeInteger shl && shl.isConstant() ) { // Shl of 0. - if( (shl.value()&63)==0 && lhs._type instanceof TypeInteger ) + if( (shl.value()&63)==0 && forceInt(lhs) && lhs._type instanceof TypeInteger ) return lhs; // (x + c) << i => (x << i) + (c << i) if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) { long sum = c.value() << shl.value(); if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) - return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) ); + return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), com.seaofnodes.simple.Parser.con(sum) ); } } diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 0023431f..4ce5f5b4 100644 --- a/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -5,9 +5,8 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class ShrNode extends ArithNode { - public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } - @Override public String label() { return "Shr"; } @Override public String op() { return ">>>"; } @Override long doOp( long x, long y ) { return x >>> y; } @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { @@ -21,7 +20,7 @@ public class ShrNode extends ArithNode { Type t2 = rhs._type; // Shr of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && lhs._type instanceof TypeInteger ) + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && forceInt(lhs) && lhs._type instanceof TypeInteger ) return lhs; // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5) diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 47f12d82..a4abaa1d 100644 --- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -43,6 +43,7 @@ public class StoreNode extends MemOpNode { @Override public Type compute() { + if( _alias==1 ) return TypeMem.BOT; Type val = val()._type; assert !_declaredType.isFRef(); Type t = val.join(_declaredType).meet(MemMergeNode.contents(mem(),_alias,this)); @@ -51,6 +52,15 @@ public class StoreNode extends MemOpNode { @Override public Node idealize() { + if( _alias==1 ) { + if( !(ptr()._type instanceof TypeMemPtr ptr) || ptr._obj._open ) return null; + Field field = ptr._obj.field(_name); + if( field==null ) return null; + Node value = new ConvertNode(field._t,val()).peephole(); + StoreNode store = new StoreNode(_loc,_name,field._alias,field._t,mem(),ptr(),off(),value,_init); + store.setDef(0,in(0)); + return new MemMergeNode(mem(),field._alias,store.peephole()); + } if( mem() instanceof MemMergeNode merge ) { setDef(1,CodeGen.CODE.add(merge.alias(_alias))); return this; @@ -138,8 +148,8 @@ public class StoreNode extends MemOpNode { if( ctrl==null ) return null; Node mem = phi.in(1); - Node init = new LoadNode(_loc,_name,_alias,_declaredType,mem,ptr(),off()).peephole(); - Node value = CodeGen.CODE.add(new PhiNode(_name,_declaredType,loop,init,val()).peephole()); + Node init = new LoadNode(_loc,_name,_alias,mem,ptr(),off()).peephole(); + Node value = CodeGen.CODE.add(new PhiNode(_name,loop,init,val()).peephole()); Node sink = new StoreNode(_loc,_name,_alias,_declaredType,mem,ptr(),off(),value,_init); // Break the memory cycle, killing the old store. Keep the Phi alive // even if that store was its last use, until our caller replaces it. @@ -164,10 +174,11 @@ public class StoreNode extends MemOpNode { public Parser.ParseException err() { Parser.ParseException err = super.err(); if( err != null ) return err; - if( ptr()._type == Type.TOP ) - return null; // This means we have an error input, report elsewhere - TypeMemPtr tmp = (TypeMemPtr)ptr()._type; - if( (tmp._ro || tmp._obj.field(_name)._final) && !_init ) + if( !(ptr()._type instanceof TypeMemPtr tmp) ) + return null; // Report the bad or unresolved receiver at its source. + Field field = tmp._obj.field(_name); + if( field==null ) return Parser.error("Cannot resolve field '"+_name+"'",_loc); + if( (tmp._ro || field._final) && !_init ) return Parser.error("Cannot modify final field '"+_name+"'",_loc); return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/StructNode.java b/src/main/java/com/seaofnodes/simple/node/StructNode.java index 78b20e9f..a91ee5de 100644 --- a/src/main/java/com/seaofnodes/simple/node/StructNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StructNode.java @@ -10,9 +10,18 @@ import com.seaofnodes.simple.util.SB; */ public class StructNode extends Node { - public final TypeStruct _ts; + public TypeStruct _ts; public StructNode(TypeStruct ts) { _ts=ts; assert !ts._open; } + @Override boolean _upgradeType(java.util.HashMap<String,Type> types) { + if( _ts==null ) return false; + TypeStruct ts = (TypeStruct)_ts.upgradeType(types); + if( ts==_ts ) return false; + unlock(); + _ts = ts; + return true; + } + @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/src/main/java/com/seaofnodes/simple/node/SubFNode.java index 3fc4f7ca..d6a908ca 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubFNode.java @@ -7,6 +7,7 @@ import java.util.BitSet; public class SubFNode extends ArithFNode { public SubFNode(Node lhs, Node rhs) { super(lhs, rhs); } + public SubFNode(ArithNode n) { super(n); } @Override public String label() { return "SubF"; } diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java index 2c4fed39..c47ffb3b 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -6,12 +6,14 @@ import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class SubNode extends ArithNode { + @Override boolean allowFloat() { return true; } public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public SubNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs,mode); } - @Override public String label() { return "Sub"; } @Override public String op() { return "-"; } - @Override long doOp( long x, long y ) { return x - y; } + @Override long doOp( long x, long y ) { return x - y; } + @Override double doOp(double x, double y ) { return x - y; } @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { // Sub of same is 0 if( in(1)==in(2) ) @@ -32,8 +34,8 @@ public class SubNode extends ArithNode { Type t1 = lhs._type; Type t2 = rhs._type; - // Keep invalid operands for type checking. - if( t1 instanceof TypeInteger x && t2 instanceof TypeInteger y ) { + // Keep invalid operands and unresolved numeric modes for type checking. + if( _mode==1 && t1 instanceof TypeInteger x && t2 instanceof TypeInteger y ) { if( y.isConstant() && y.value()==0 ) return lhs; if( x.isConstant() && x.value()==0 ) return new MinusNode(rhs); } @@ -49,6 +51,5 @@ public class SubNode extends ArithNode { return super.idealize(); } - @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); } - @Override Node copyF() { return new SubFNode(null,null); } + @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs,_mode); } } diff --git a/src/main/java/com/seaofnodes/simple/node/XorNode.java b/src/main/java/com/seaofnodes/simple/node/XorNode.java index 75cea98e..e3dac2d0 100644 --- a/src/main/java/com/seaofnodes/simple/node/XorNode.java +++ b/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -5,9 +5,8 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; public class XorNode extends ArithNode { - public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } + public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } - @Override public String label() { return "Xor"; } @Override public String op() { return "^"; } @Override long doOp( long x, long y ) { return x ^ y; } @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { @@ -18,12 +17,13 @@ public class XorNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; // Xor of 0. We do not check for (0^x) because this will already // canonicalize to (x^0) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && lhs._type instanceof TypeInteger ) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && forceInt(lhs) && lhs._type instanceof TypeInteger ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java index e1717cb3..b6d82f09 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -15,7 +15,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize { assert tfp.isConstant(); _tfp = tfp; FunNode fun = CodeGen.CODE.link(tfp); - _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls + _name = fun==null ? CodeGen.CODE.externFunc(tfp) : fun._name; } @Override public String op() { return "call"; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index fae98a99..0d5b5440 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -359,7 +359,7 @@ public class arm extends Machine { String op = "!="; if( iff.in(1) instanceof BoolNode bool ) op = bool.op(); else if( iff.in(1)==null ) op = "=="; // Never-node cutout - else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO),(byte)1)); return new BranchARM(iff, op); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index f0ad8e88..03bedd0d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -16,7 +16,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { _inputs.pop(); // Pop constant target _tfp = tfp; FunNode fun = CodeGen.CODE.link(tfp); - _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls + _name = fun==null ? CodeGen.CODE.externFunc(tfp) : fun._name; } @Override public String op() { return "call"; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java index d1a43b83..cd4d2452 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -15,7 +15,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize { assert tfp.isConstant(); _tfp = tfp; FunNode fun = CodeGen.CODE.link(tfp); - _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls + _name = fun==null ? CodeGen.CODE.externFunc(tfp) : fun._name; } @Override public String op() { return "call"; } @Override public String label() { return op(); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index fcae17f8..f0598422 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -441,7 +441,7 @@ public class x86_64_v2 extends Machine { String op = "!="; if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op(); else if( iff.in(1)==null ) op = "=="; // Never-node cutout - else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO),(byte)1)); return new JmpX86(iff, op); } diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java index 1f22cb05..1396f1de 100644 --- a/src/main/java/com/seaofnodes/simple/type/Field.java +++ b/src/main/java/com/seaofnodes/simple/type/Field.java @@ -105,7 +105,7 @@ public class Field extends Type { Type glb = _t._glb(true); return (glb== _t && _final ) ? this : make(_fname,glb,_alias,true,_one); } - @Override Field _close() { return makeFrom( _t._close()); } + @Override Field _close(java.util.HashMap<String,Type> types) { return makeFrom( _t._close(types)); } // Override in subclasses int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0) ^ (_one ? 2048 : 0); } diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java index b9a6a1f3..9fad2767 100644 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -57,6 +57,7 @@ public class Type /*implements Cloneable*/ { static final byte TNIL = 4; // low null of all flavors static final byte TXNIL = 5; // high or choice null static final byte TSIMPLE = 6; // End of the Simple Types + static final byte TSCALAR = 6; // All register/field values, excluding control and memory static final byte TPTR = 7; // All nil-able scalar values static final byte TINT = 8; // All Integers; see TypeInteger @@ -97,6 +98,7 @@ public class Type /*implements Cloneable*/ { ts.add(BOTTOM); ts.add(CONTROL); ts.add(NIL); + TypeScalar.gather(ts); TypeNil.gather(ts); TypePtr.gather(ts); TypeInteger.gather(ts); @@ -267,6 +269,8 @@ public class Type /*implements Cloneable*/ { // Constant arrays meet their integer element types. if( this instanceof TypeConAry tcon && t instanceof TypeInteger ti ) return tcon.ymeet(ti); if( t instanceof TypeConAry tcon && this instanceof TypeInteger ti ) return tcon.ymeet(ti); + if( this instanceof TypeScalar scalar0 && t instanceof TypeScalar scalar1 ) + return scalar0.smeet(scalar1); return Type.BOTTOM; // Mixing 2 unrelated types } @@ -280,8 +284,14 @@ public class Type /*implements Cloneable*/ { if( _type==TTOP || t._type==TBOT ) return t; // RHS TypeNil vs NIL/XNIL - if( _type== TNIL ) return t instanceof TypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? TypePtr.PTR : BOTTOM); - if( _type== TXNIL ) return t instanceof TypeNil ptr ? ptr.meetX() : (t._type== TNIL ? TypePtr.PTR : BOTTOM); + if( _type==TNIL || _type==TXNIL ) { + if( t instanceof TypeNil ptr ) return _type==TNIL ? ptr.meet0() : ptr.meetX(); + if( t instanceof TypeScalar ) { + if( t._type==TSCALAR ) return t.isHigh() ? this : t; + return TypeScalar.BOT; + } + return t._type==(_type==TNIL ? TXNIL : TNIL) ? TypePtr.PTR : BOTTOM; + } // 'this' is only {TCTRL,TXCTRL} // Other non-simple RHS things bottom out @@ -487,7 +497,7 @@ public class Type /*implements Cloneable*/ { } Type _glb(boolean mem) { assert is_simple(); return Type.BOTTOM; } - Type _close() { return this; } + Type _close(HashMap<String,Type> types) { return this; } public Type widen() { return this; } // ---------------------------------------------------------- @@ -523,4 +533,11 @@ public class Type /*implements Cloneable*/ { // This is used by error messages, and is a shorted print. public String str() { return STRS[_type]; } + + // Closing declarations changes the meaning of forward shapes. Upgrade + // the whole graph together before starting the pessimistic fixed point. + public final Type upgradeType(HashMap<String,Type> types) { + return recurOpen()._upgradeType(types).recurClose(); + } + Type _upgradeType(HashMap<String,Type> types) { return this; } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java index 0bc264fc..d7ce8503 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java @@ -7,7 +7,7 @@ import java.util.ArrayList; /** * Represents a constant array of primitives */ -public abstract class TypeConAry<A> extends Type { +public abstract class TypeConAry<A> extends TypeScalar { boolean _any; byte _widen; // One of byte or int array diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java index f3de581c..bb00ee91 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java @@ -8,7 +8,7 @@ import java.util.ArrayList; /** * Float Type */ -public class TypeFloat extends Type { +public class TypeFloat extends TypeScalar { // - high -64, high -32, con 0, low +32, low +64 public byte _sz; diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index 50743f69..602aaaae 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -157,13 +157,13 @@ public class TypeFunPtr extends TypeNil { @Override boolean _isFinal() { return true; } @Override boolean _isGLB(boolean mem) { return true; } @Override TypeFunPtr _glb(boolean mem) { return this; } - @Override TypeFunPtr _close() { + @Override TypeFunPtr _close(java.util.HashMap<String,Type> types) { Type[] sig = new Type[_sig.length]; TypeFunPtr fun = malloc(_nil,false,sig,null,_fidxs); // Now start the recursion - fun._ret = _ret._close(); + fun._ret = _ret._close(types); for( int i=0; i<sig.length; i++ ) - sig[i] = _sig[i]._close(); + sig[i] = _sig[i]._close(types); return fun; } @@ -266,4 +266,15 @@ public class TypeFunPtr extends TypeNil { // Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) ) { int fidx = Long.numberOfTrailingZeros(fidxs); ... } public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); } + + @Override Type _upgradeType(java.util.HashMap<String,Type> types) { + TypeFunPtr old = (TypeFunPtr)VISIT.get(_uid); + if( old!=null ) return old; + Type[] sig = new Type[_sig.length]; + TypeFunPtr fun = malloc(_nil,_open,sig,null,_fidxs); + VISIT.put(_uid,fun); + fun._ret = _ret._upgradeType(types); + for( int i=0; i<sig.length; i++ ) sig[i] = _sig[i]._upgradeType(types); + return fun; + } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java index 74fbfb9f..1b0019d4 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -8,7 +8,7 @@ import java.util.ArrayList; /** * Integer Type */ -public class TypeInteger extends Type { +public class TypeInteger extends TypeScalar { /** * Describes an integer *range* - everything from min to max; both min and * max are inclusive. If min==max, this is a constant. diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/src/main/java/com/seaofnodes/simple/type/TypeMem.java index d5ee8bd8..02cfc57b 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeMem.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeMem.java @@ -59,7 +59,10 @@ public class TypeMem extends Type { if( that==TOP ) return this; if( this==BOT ) return BOT; if( that==BOT ) return BOT; - int alias = _alias==that._alias ? _alias : 1; + // Different slices lose their alias and contents together. Their + // scalar meet alone cannot describe the unsplit bulk memory. + if( _alias!=that._alias ) return BOT; + int alias = _alias; Type mt = _t.meet(that._t); return make(alias,mt); } @@ -107,4 +110,8 @@ public class TypeMem extends Type { } @Override public String str() { return toString(); } + + @Override Type _upgradeType(java.util.HashMap<String,Type> types) { + return make(_alias,_t._upgradeType(types)); + } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java index 9fc90544..4a28b919 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -126,7 +126,7 @@ public class TypeMemPtr extends TypeNil { @Override TypeMemPtr _makeRO() { return withAccess(true); } @Override boolean _isGLB(boolean mem) { return _ro && _nil==3 && !_one && _obj.isGLB2(); } @Override TypeMemPtr _glb(boolean mem) { return make((byte)3,_obj.glb2(),false,true); } - @Override TypeMemPtr _close() { return makeFrom(_obj._close()); } + @Override TypeMemPtr _close(HashMap<String,Type> types) { return makeFrom(_obj._close(types)); } // True if this "isa" t up to named structures @@ -174,4 +174,8 @@ public class TypeMemPtr extends TypeNil { if( this== NOTBOT) return sb.p("*void"); return _obj.print(sb.p(x()).p("*"),visit,html).p(q()); } + + @Override Type _upgradeType(java.util.HashMap<String,Type> types) { + return makeFrom((TypeStruct)_obj._upgradeType(types)); + } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeNil.java b/src/main/java/com/seaofnodes/simple/type/TypeNil.java index 56d73b00..32505aa2 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeNil.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeNil.java @@ -7,7 +7,7 @@ import java.util.ArrayList; /** * Nil-able Scalar types */ -public abstract class TypeNil extends Type { +public abstract class TypeNil extends TypeScalar { // 0 = high-subclass choice nil // 1 = high-subclass no nil // 2 = low -subclass no nil diff --git a/src/main/java/com/seaofnodes/simple/type/TypeScalar.java b/src/main/java/com/seaofnodes/simple/type/TypeScalar.java new file mode 100644 index 00000000..1b41db13 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/type/TypeScalar.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.SB; +import java.util.ArrayList; +import java.util.BitSet; + +/** + * Values that fit in a register or field: integers, floats and pointers. + * These bounds exclude control and memory. An unresolved load can produce a + * scalar without making the parser guess which kind of value it will hold. + */ +public class TypeScalar extends Type { + protected TypeScalar(byte type) { super(type); } + private TypeScalar() { this(TSCALAR); } + public static final TypeScalar BOT = new TypeScalar().intern(); + public static final TypeScalar TOP = BOT.dual(); + public static void gather(ArrayList<Type> ts) { ts.add(BOT); } + + final Type smeet(TypeScalar scalar) { + if( _type==TSCALAR ) return isHigh() ? scalar : this; + if( scalar._type==TSCALAR ) return scalar.isHigh() ? this : scalar; + return BOT; + } + @Override Type xmeet(Type t) { return isHigh() ? t : this; } + @Override TypeScalar xdual() { return new TypeScalar(); } + @Override public boolean isHigh() { return this==TOP; } + @Override boolean _isGLB(boolean mem) { return this==BOT; } + @Override Type _glb(boolean mem) { return BOT; } + @Override public String str() { return isHigh() ? "~scalar" : "scalar"; } + @Override SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); } +} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index 55942b0e..8eec6dd4 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -90,18 +90,27 @@ public class TypeStruct extends Type { public final TypeStruct close() { - return (TypeStruct)recurOpen()._close().recurClose(); + return close(null); } - @Override TypeStruct _close() { + public final TypeStruct close(HashMap<String,Type> types) { + return (TypeStruct)recurOpen()._close(types).recurClose(); + } + @Override TypeStruct _close(HashMap<String,Type> types) { Object key = isAry() ? _uid : _name; TypeStruct ts = (TypeStruct)VISIT.get(key); if( ts!=null ) return ts; + if( _open && types!=null && types.get(_name) instanceof TypeMemPtr ptr && !ptr._obj._open ) + return ptr._obj._close(types); + // Closing A must not pretend that an undeclared B is an empty + // definition. The root is visited first; self references find it above. + // Keep other empty forward shapes open until their own declaration. + if( _open && _fields.length==0 && VISIT.size()>1 ) return this; ts = recurPre(key,false); Field[] flds = ts._fields; // Now start the recursion for( int i=0; i<flds.length; i++ ) - flds[i].setType(_fields[i]._t._close()); + flds[i].setType(_fields[i]._t._close(types)); return ts; } @@ -490,4 +499,16 @@ public class TypeStruct extends Type { return is; } + + @Override Type _upgradeType(HashMap<String,Type> types) { + Type def = types.get(_name); + if( def instanceof TypeMemPtr ptr && !ptr._obj._open && _open ) + return ptr._obj; + TypeStruct old = (TypeStruct)VISIT.get(_uid); + if( old!=null ) return old; + TypeStruct ts = recurPre(_uid,_open); + for( int i=0; i<_fields.length; i++ ) + ts._fields[i].setType(_fields[i]._t._upgradeType(types)); + return ts; + } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java index df9aba31..27a0a9d9 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java @@ -18,6 +18,7 @@ public class TypeTuple extends Type { // All fields directly listed public static TypeTuple make(Type... types) { TypeTuple tt = malloc(types); + for( Type t : types ) if( !t._terned ) return tt; TypeTuple t2 = tt.intern(); return t2==tt ? tt : t2.free(tt); } @@ -61,6 +62,18 @@ public class TypeTuple extends Type { return malloc(TypeFunPtr.xdual(_types)); } + @Override int nkids() { return _types==null ? 0 : _types.length; } + @Override Type at(int idx) { return _types[idx]; } + @Override void set(int idx, Type t) { _types[idx]=t; } + @Override TypeTuple rdual() { + if( _dual!=null ) return dual(); + TypeTuple d = malloc(new Type[_types.length]); + (_dual=d)._dual=this; + for( int i=0; i<_types.length; i++ ) + d._types[i] = _types[i]._terned ? _types[i].dual() : _types[i].rdual(); + return d; + } + @Override boolean _isConstant() { for( Type t : _types ) if( !t._isConstant() ) @@ -112,8 +125,10 @@ public class TypeTuple extends Type { return true; } - @Override int nkids() { return _types.length; } - @Override Type at( int idx ) { return _types[idx]; } - @Override void set( int idx, Type t ) { _types[idx] = t; } - + @Override Type _upgradeType(java.util.HashMap<String,Type> types) { + if( _types==null ) return this; + Type[] ts = new Type[_types.length]; + for( int i=0; i<ts.length; i++ ) ts[i] = _types[i]._upgradeType(types); + return make(ts); + } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/src/test/java/com/seaofnodes/simple/Chapter07Test.java index 8db31bc1..daca77ab 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter07Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter07Test.java @@ -18,7 +18,7 @@ while(arg < 10) { return arg; """); code.parse(); - assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertEquals("return Phi(Loop,arg,(Convert_i64(Phi_arg)+1));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -71,7 +71,7 @@ while(a < 10) { return b; """); code.parse(); - assertEquals("return Phi(Loop,2,Phi(Region,Phi_b,4));", code.print()); + assertEquals("return Convert_i64(Phi(Loop,2,Phi(Region,Phi_b,4)));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -107,7 +107,7 @@ while(a < 10) { return a; """); code.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertEquals("return Phi(Loop,1,(Convert_i64(Phi_a)+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -165,7 +165,7 @@ while(a < 10) { return a; """); code.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertEquals("return Phi(Loop,1,(Convert_i64(Phi_a)+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -198,7 +198,7 @@ while(a < 10) { return a; """); code.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertEquals("return Phi(Loop,1,(Convert_i64(Phi_a)+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java index f24d96eb..50410618 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -255,7 +255,7 @@ return 0!=0; int v0=0; while(0==1) while(v0) v0=1+v0; -return 0; +return 0; """); code.driver(CodeGen.Phase.FinalizeIR); assertEquals("return 0;", code.print()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java index 2ef209c5..2a86b11a 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -212,7 +212,7 @@ return a.f0.f1.f0.f1.f0; """); code.driver(CodeGen.Phase.LocalSched); assertEquals("return B;", code._stop.toString()); - assertEquals("B{f0=C},f1=A{f0=$cyclic,f1=$cyclic}}", Eval2.eval(code, 0)); + assertEquals("B{f0=C{f0=A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0)); } @Test @@ -278,6 +278,12 @@ return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c; """); code.driver(CodeGen.Phase.LocalSched); assertEquals("return R;", code._stop.toString()); - assertEquals("R{a=C{a=N},b=V},c=H}},b=K{a=$cyclic,b=D{a=O},b=W},c=I}},c=P}},c=$cyclic}", Eval2.eval(code, 0)); + // All forward shapes are complete now. Following field a visits + // all 26 objects before closing the cycle; b and c revisit them. + String expected = "$cyclic"; + String chain = "RCNYJUFQBMXITEPALWHSDOZKVG"; + for( int i=chain.length()-1; i>=0; i-- ) + expected=chain.charAt(i)+"{a="+expected+",b=$cyclic,c=$cyclic}"; + assertEquals(expected,Eval2.eval(code,0)); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java index b0cf0a91..e9333837 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java @@ -84,7 +84,7 @@ while( 1 ) { return guess; """); code.driver(CodeGen.Phase.FinalizeIR); - assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print()); + assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print()); assertEquals("3.0", Eval2.eval(code, 9)); assertEquals("1.414213562373095", Eval2.eval(code, 2)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/src/test/java/com/seaofnodes/simple/Chapter14Test.java index b4a27c04..06d86f5f 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter14Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -176,7 +176,7 @@ while(v1) break; return v1; """); code.driver(CodeGen.Phase.FinalizeIR); - assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print()); + assertEquals("return (Phi(Loop,(((1<<arg)&1)&Shl),0)&4294967295);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index b6a0ab57..4927f7e2 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -283,7 +283,7 @@ for(;;) { } """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return #2; return Parm_i(x,i64); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Loop,arg,3); return Parm_i(x,i64); ]", code._stop.toString()); assertEquals("3", Eval2.eval(code, 0)); } @@ -454,7 +454,7 @@ if (i2i) return i2i(arg); return f2f(o)(1); """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return Phi(Region,#2,1); return Parm_i(i2i.o,i64); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,arg,1); return Parm_i(i2i.o,i64); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java index 303cfda2..41ec6cef 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -80,7 +80,7 @@ val _hashCodeString = { !String self -> }; """; CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LocalSched); - assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,(!=0)._hashCode,Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString()); //assertEquals("-4898613127354160978", Eval2.eval(code, 2)); } @@ -362,7 +362,7 @@ val test_sqrt = { flt x -> flt farg = arg; return test_sqrt(farg); """; CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); - assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,(divf,cvtf,Phi_guess),Phi_guess),0.5f));", code.print()); + assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,Phi_guess,(divf,cvtf,Phi_guess)),0.5f));", code.print()); }; diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java index ba93a440..fe69e5ad 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -285,9 +285,9 @@ while (arg > 0) { } return arg; """; - testTarget(src,"x86_64_v2", "SystemV",3,null); - testTarget(src,"riscv" , "SystemV",2,null); - testTarget(src,"arm" , "SystemV",2,null); + testTarget(src,"x86_64_v2", "SystemV",1,null); + testTarget(src,"riscv" , "SystemV",0,null); + testTarget(src,"arm" , "SystemV",0,null); } // Original Chapter 20 allocation workload, kept fixed for cohort comparisons. @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java index 1bf65cfd..8442bbc8 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -467,9 +467,17 @@ while(true) { } return i; """; - testCPU(src,"x86_64_v2", "Win64" ,0,"return (inc,Phi(Loop,0,inc));"); - testCPU(src,"riscv" , "SystemV",0,"return ( Phi(Loop,0,addi) + #1 );"); - testCPU(src,"arm" , "SystemV",0,"return (inc,Phi(Loop,0,inc));"); + testCPU(src,"x86_64_v2", "Win64" ,0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));"); + testCPU(src,"riscv" , "SystemV",0,"return (!=0)( Phi(Loop,0,Phi(Region,(!=0),(!=0),0)) + #1 );"); + testCPU(src,"arm" , "SystemV",0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));"); + // Guards now survive into selection. They must preserve the loop's + // value through both continues and the final break without code. + EvalRisc5 r5 = TestRisc5.build("guardLoop",src,0,0,false); + assertEquals(0,r5.step(1000)); + assertEquals(2,r5.regs[riscv.A0]); + EvalArm64 a5 = TestArm64.build("guardLoop",src,0,0,false); + assertEquals(0,a5.step(1000)); + assertEquals(2,a5.regs[0]); } @Test public void testNewtonExport() throws IOException { @@ -498,7 +506,7 @@ val test_sqrt = { flt x -> } }; """; - TestC.run(src, "newtonFloat", null, result, 34); + TestC.run(src, "newtonFloat", null, result, TestC.CALL_CONVENTION.equals("Win64") ? 42 : 34); EvalRisc5 R5 = TestRisc5.build("newtonFloat",src, 0, 10, false); R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0; diff --git a/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/src/test/java/com/seaofnodes/simple/Chapter23Test.java index 22be3af1..1dd21d08 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter23Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter23Test.java @@ -115,7 +115,7 @@ var apply = { i64 x, { i64 -> u32 } fcn -> """ int a = 1; int b = 0; - + if(a++ || b++ ) { if(b == 0 && a == 2) { sys.io.p("Or"); @@ -149,7 +149,7 @@ var apply = { i64 x, { i64 -> u32 } fcn -> """ int a = 1; int b = 1; - + if(a && b) { sys.io.p("And"); } else { @@ -186,14 +186,14 @@ var apply = { i64 x, { i64 -> u32 } fcn -> String src2 = """ struct S { !S? !fld; }; - + val ptr = arg == 1 ? null : new S{fld = arg==1 ? null : new S{fld = null;};}; if( ptr && ptr.fld ) { sys.io.p("true"); } else { sys.io.p("false"); } - + return 0; """; CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); @@ -221,13 +221,13 @@ var apply = { i64 x, { i64 -> u32 } fcn -> """ int a = 1; int b = 1; - + int x=1; int y=1; int z=0; - + int g = x++ && y++ && z++; - + if(x == 2 && y == 2 && z == 1 && g == 0) { sys.io.p("Effected"); } else { @@ -259,13 +259,13 @@ var apply = { i64 x, { i64 -> u32 } fcn -> """ int a = 1; int b = 1; - + int x = -1; int y=1; int z= -1; - + int g = x++ || y++ || z++; - + int switch = 0; if(x == 4 || y == 4 || z == 4) { switch = -1; @@ -305,11 +305,11 @@ var apply = { i64 x, { i64 -> u32 } fcn -> // -*- mode: java; -*- int a = 1; int b = 1; - + var sq = { int x -> x*x; }; - + if(a && sq(0)) { sys.io.p("And"); } else { diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java index 98e0f18a..58b3b74e 100644 --- a/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/src/test/java/com/seaofnodes/simple/Eval2.java @@ -254,23 +254,24 @@ public abstract class Eval2 { private static Object compute( Node n ) { return switch( n ) { case AddFNode adf -> d(adf.in(1)) + d(adf.in(2)); - case AddNode add -> x(add.in(1)) + x(add.in(2)); + case AddNode add -> add.mode()==2 ? (Object)(d(add.in(1)) + d(add.in(2))) : (Object)(x(add.in(1)) + x(add.in(2))); case AndNode and -> x(and.in(1)) & x(and.in(2)); case BoolNode.EQF eqf -> d(eqf.in(1)) == d(eqf.in(2)) ? 1L : 0L; case BoolNode.LEF lef -> d(lef.in(1)) <= d(lef.in(2)) ? 1L : 0L; case BoolNode.LTF ltf -> d(ltf.in(1)) < d(ltf.in(2)) ? 1L : 0L; case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; // Bool EQ supports pointers, nil, and integers - case BoolNode.LE le -> x(le .in(1)) <= x(le .in(2)) ? 1L : 0L; - case BoolNode.LT lt -> x(lt .in(1)) < x(lt .in(2)) ? 1L : 0L; + case BoolNode.LE le -> (le.isFloat() ? d(le.in(1))<=d(le.in(2)) : x(le.in(1))<=x(le.in(2))) ? 1L : 0L; + case BoolNode.LT lt -> (lt.isFloat() ? d(lt.in(1))<d(lt.in(2)) : x(lt.in(1))<x(lt.in(2))) ? 1L : 0L; case CastNode cast -> val(cast.in(1)); + case GuardNode guard -> val(guard.in(1)); case ConstantNode con -> con(con._con); case DivFNode dvf -> d(dvf.in(2))==0 ? 0D : d(dvf.in(1)) / d(dvf.in(2)); - case DivNode div -> x(div.in(2))==0 ? 0L : x(div.in(1)) / x(div.in(2)); + case DivNode div -> div.mode()==2 ? (Object)(d(div.in(2))==0 ? 0D : d(div.in(1))/d(div.in(2))) : (Object)(x(div.in(2))==0 ? 0L : x(div.in(1))/x(div.in(2))); case LoadNode ld -> load(ld); case MinusFNode mnf -> - d(mnf.in(1)); - case MinusNode sub -> - x(sub.in(1)); + case MinusNode sub -> sub.mode()==2 ? (Object)(-d(sub.in(1))) : (Object)(-x(sub.in(1))); case MulFNode mlf -> d(mlf.in(1)) * d(mlf.in(2)); - case MulNode mul -> x(mul.in(1)) * x(mul.in(2)); + case MulNode mul -> mul.mode()==2 ? (Object)(d(mul.in(1)) * d(mul.in(2))) : (Object)(x(mul.in(1)) * x(mul.in(2))); case NewNode alloc-> alloc(alloc); case NotNode not -> not(not); case OrNode or -> x(or .in(1)) | x(or .in(2)); @@ -281,7 +282,7 @@ public abstract class Eval2 { case ShlNode shl -> x(shl.in(1)) << x(shl.in(2)); case ShrNode shr -> x(shr.in(1)) >>>x(shr.in(2)); case StoreNode st -> store(st); - case SubNode sub -> x(sub.in(1)) - x(sub.in(2)); + case SubNode sub -> sub.mode()==2 ? (Object)(d(sub.in(1)) - d(sub.in(2))) : (Object)(x(sub.in(1)) - x(sub.in(2))); case SubFNode sbf -> d(sbf.in(1)) - d(sbf.in(2)); case RoundF32Node rnd -> (double)(float)d(rnd.in(1)); case ToIntegerNode cvt -> (long)d(cvt.in(1)); diff --git a/src/test/java/com/seaofnodes/simple/IncompleteTypesTest.java b/src/test/java/com/seaofnodes/simple/IncompleteTypesTest.java new file mode 100644 index 00000000..f2f7d882 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/IncompleteTypesTest.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import com.seaofnodes.simple.type.TypeInteger; +import static org.junit.Assert.assertEquals; + +public class IncompleteTypesTest { + @Test public void testForwardFieldAndNumericMode() { + // At the addition, neither the field's type nor its offset is known. + String src = """ + val add = { Later p -> return p.x + 1; }; + struct Later { flt x; }; + return add(new Later { x = 2.5; }); + """; + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("3.5",Eval2.eval(code,0)); + } + + @Test public void testForwardStore() { + // Resolve the field's alias and integer width after its declaration. + String src = """ + val set = { !Later p, int n -> p.x = n; return p.x; }; + struct Later { u8 x; }; + return set(new Later,258); + """; + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("2",Eval2.eval(code,0)); + for( String update : new String[]{"p.x += n","p.x++"} ) { + String updates = "val set = { !Later p, int n -> p.x=255; return "+update+"; }; " + + "struct Later { u8 x; }; return set(new Later,3);"; + // Seeds 2 and 13 let the Load resolve before the symbolic Store. + // It must wait for that Store's alias, not read an earlier Store. + for( long seed : new long[]{2,13,123} ) { + var result = new CodeGen(updates,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(update.equals("p.x++") ? "255" : "2",Eval2.eval(result,0)); + } + } + } +} diff --git a/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/src/test/java/com/seaofnodes/simple/MergeSortTest.java index 453bd35c..324790a0 100644 --- a/src/test/java/com/seaofnodes/simple/MergeSortTest.java +++ b/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -17,13 +17,13 @@ public class MergeSortTest { @Test public void testMergeSort0() throws IOException { // Test merge sort on a small array of primes String src = -""" +""" val merge_sort = { int[] a -> int[] !b = new int[a#]; copy_array (a, 0, a#, b); split_merge(a, 0, a#, b); }; - + val split_merge = { int[] !b, int begin, int end, int[] a -> if (end - begin <= 1) return 0; @@ -33,10 +33,10 @@ public class MergeSortTest { merge(b, begin, middle, end, a); return 0; }; - + val merge = { int[] !b, int begin, int middle, int end, int[] a -> int i = begin, j = middle; - + for (int k = begin; k < end; k++) { // && and || bool cond = false; @@ -48,7 +48,7 @@ public class MergeSortTest { else b[k] = a[j++]; } }; - + val copy_array = { int[] !a, int begin, int end, int[] b -> for (int k = begin; k < end; k++) b[k] = a[k]; diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java index fd3fea9d..c7ba8caa 100644 --- a/src/test/java/com/seaofnodes/simple/TypeTest.java +++ b/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -47,8 +47,8 @@ public class TypeTest { // Test basic properties and GLB @Test public void testTypeAdHoc() { - Assert.assertEquals( Type.BOTTOM, TypeInteger.TRUE.meet(TypeNil.NIL) ); - Assert.assertEquals( Type.BOTTOM, TypeInteger.TOP .meet(TypeNil.NIL) ); + Assert.assertEquals( TypeScalar.BOT, TypeInteger.TRUE.meet(TypeNil.NIL) ); + Assert.assertEquals( TypeScalar.BOT, TypeInteger.TOP .meet(TypeNil.NIL) ); TypeStruct s1 = TypeStruct.make("s1",false, Field.make("a", TypeInteger.BOT,-1, false, false), diff --git a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java index 0304c6a1..7a21c3a7 100644 --- a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java +++ b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java @@ -118,7 +118,7 @@ public class RegAllocTestSupport { @Override public boolean commutes() { return true; } }; RegionNode region = new RegionNode(null,code._start,code._start); - new PhiNode("sum",TypeInteger.BOT,region,add,a); + new PhiNode("sum",region,add,a); code._cfg.add(region); assertTrue(BuildLRG.run(0,new RegAlloc(code))); } @@ -185,7 +185,7 @@ public class RegAllocTestSupport { Op def = new Op(A,null,null,false,code._start); Node top = new ConstantNode(com.seaofnodes.simple.type.Type.TOP); RegionNode region = new RegionNode(null,code._start,code._start); - PhiNode phi = new PhiNode("value",TypeInteger.BOT,region,slot==1 ? top : def,slot==2 ? top : def); + PhiNode phi = new PhiNode("value",region,slot==1 ? top : def,slot==2 ? top : def); phi._type = TypeInteger.BOT; new Op(null,A,null,false,region,phi); code._cfg.add(region); @@ -255,7 +255,7 @@ public class RegAllocTestSupport { RegAlloc alloc = new RegAlloc(code); Op init = new Op(A,null,null,false,code._start); LoopNode loop = new LoopNode(null,code._start); - PhiNode phi = new PhiNode("loop",TypeInteger.BOT,loop,init,null); + PhiNode phi = new PhiNode("loop",loop,init,null); Op back = new Op(A,A,null,false,loop,phi); phi.setDef(2,back); Op use = new Op(null,A,null,false,loop,phi); |
