1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
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.
// Optimistic SCCP must retain stronger facts already inside that family.
@Override public Type compute() {
Type src = val()._type;
if( CodeGen.CODE._phase == CodeGen.Phase.Opto ) {
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 boolean _upgradeType(HashMap<String,Type> TYPES) {
Type dst = _dst.upgradeType(TYPES);
if( dst==_dst ) return false;
unlock();
_dst = dst;
return true;
}
@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(); }
}
|