forked from pharo-project/pharo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
OCASTSemanticAnalyzer.class.st
240 lines (198 loc) · 7.57 KB
/
OCASTSemanticAnalyzer.class.st
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
"
I visit each node in the abstract syntax tree while growing and shrinking a scope chain. Each method and block node is linked with its corresponding scope object, and each variable def and ref is linked with its corresponding OCVariable. Exceptions are raised for undefined variable references and so on (see subclasses of OCSemanticWarning).
"
Class {
#name : #OCASTSemanticAnalyzer,
#superclass : #RBProgramNodeVisitor,
#instVars : [
'scope',
'blockcounter',
'compilationContext'
],
#category : #'OpalCompiler-Core-Semantics'
}
{ #category : #variables }
OCASTSemanticAnalyzer >> analyseEscapingRead: var [
(var scope outerNotOptimizedScope ~= scope outerNotOptimizedScope ) ifFalse: [ ^self ].
"only escaping when they will end up in different closures"
var markEscapingRead.
"if we read a variable in a loop that is a repeated write, it need to be marked as escaping write"
(scope isInsideOptimizedLoop and: [var isRepeatedWrite])
ifTrue: [var markEscapingWrite]
]
{ #category : #variables }
OCASTSemanticAnalyzer >> analyseEscapingWrite: var [
(var scope outerNotOptimizedScope ~= scope outerNotOptimizedScope)
"only escaping when they will end up in different closures"
ifTrue: [ var markEscapingWrite].
"if we write a variable in a loop, mark it as a repeated Write"
scope isInsideOptimizedLoop
ifTrue: [ var markRepeatedWrite ]
ifFalse: [ var markWrite ]
]
{ #category : #api }
OCASTSemanticAnalyzer >> analyze: aNode [
self visitNode: aNode.
OCASTClosureAnalyzer new visitNode: aNode
]
{ #category : #accessing }
OCASTSemanticAnalyzer >> blockcounter [
^blockcounter ifNil: [0]
]
{ #category : #accessing }
OCASTSemanticAnalyzer >> compilationContext [
^ compilationContext
]
{ #category : #accessing }
OCASTSemanticAnalyzer >> compilationContext: aCompilationContext [
compilationContext := aCompilationContext
]
{ #category : #variables }
OCASTSemanticAnalyzer >> declareArgumentNode: aVariableNode [
^self declareVariableNode: aVariableNode as: OCArgumentVariable new
]
{ #category : #variables }
OCASTSemanticAnalyzer >> declareVariableNode: aVariableNode [
^self declareVariableNode: aVariableNode as: OCTempVariable new
]
{ #category : #variables }
OCASTSemanticAnalyzer >> declareVariableNode: aVariableNode as: anOCTempVariable [
| name var |
name := aVariableNode name.
var := scope lookupVarForDeclaration: name.
var ifNotNil: [
"Another variable with same name is visible from current scope.
Warn about the name clash and if proceed add new temp to shadow existing var"
self variable: aVariableNode shadows: var ].
var := scope addTemp: anOCTempVariable withName: name.
aVariableNode binding: var.
^ var
]
{ #category : #variables }
OCASTSemanticAnalyzer >> lookupVariableForRead: aVariableNode [
| var |
var := scope lookupVar: aVariableNode name.
var ifNil: [^var].
var isTemp ifTrue: [ self analyseEscapingRead: var].
^var
]
{ #category : #variables }
OCASTSemanticAnalyzer >> lookupVariableForWrite: aVariableNode [
| var |
var := scope lookupVar: aVariableNode name.
var ifNil: [^var].
var isReservedVariable ifTrue: [ self storeIntoReservedVariable: aVariableNode ].
var isWritable ifFalse: [ self storeIntoReadOnlyVariable: aVariableNode ].
var isTemp ifTrue: [ self analyseEscapingWrite: var ].
^var
]
{ #category : #initialization }
OCASTSemanticAnalyzer >> scope: aSemScope [
scope := aSemScope
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> storeIntoReadOnlyVariable: variableNode [
compilationContext optionSkipSemanticWarnings ifTrue: [ ^ self ].
^ OCStoreIntoReadOnlyVariableError new
node: variableNode;
compilationContext: compilationContext;
messageText: 'Cannot store into';
signal
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> storeIntoReservedVariable: variableNode [
compilationContext optionSkipSemanticWarnings ifTrue: [ ^ self ].
^ OCStoreIntoReservedVariableError new
node: variableNode;
compilationContext: compilationContext;
messageText: 'Cannot store into';
signal
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> undeclaredVariable: variableNode [
compilationContext optionSkipSemanticWarnings
ifTrue: [ ^UndeclaredVariable named: variableNode name asSymbol ].
^ OCUndeclaredVariableWarning new
node: variableNode;
compilationContext: compilationContext;
signal
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> uninitializedVariable: variableNode [
variableNode binding markRead.
variableNode propertyAt: #semanticWarning put: #unitialized.
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> unusedVariable: variableNode [
variableNode propertyAt: #semanticWarning put: 'unused variable'
]
{ #category : #'error handling' }
OCASTSemanticAnalyzer >> variable: variableNode shadows: semVar [
compilationContext optionSkipSemanticWarnings ifTrue: [ ^semVar ].
^ OCShadowVariableWarning new
node: variableNode;
shadowedVar: semVar;
compilationContext: compilationContext;
signal
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitAssignmentNode: anAssignmentNode [
| var |
self visitNode: anAssignmentNode value.
var := (self lookupVariableForWrite: anAssignmentNode variable)
ifNil: [ self undeclaredVariable: anAssignmentNode variable ].
anAssignmentNode variable binding: var
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitBlockNode: aBlockNode [
blockcounter := self blockcounter + 1.
aBlockNode isInlined ifTrue: [^ self visitInlinedBlockNode: aBlockNode ].
scope := scope newBlockScope: blockcounter.
aBlockNode scope: scope. scope node: aBlockNode.
aBlockNode arguments do: [:node | self declareArgumentNode: node ].
self visitNode: aBlockNode body.
scope := scope popScope.
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitInlinedBlockNode: aBlockNode [
scope := scope newOptimizedBlockScope: blockcounter.
aBlockNode isInlinedLoop ifTrue: [scope markInlinedLoop].
aBlockNode scope: scope. scope node: aBlockNode.
aBlockNode arguments do: [:node | self declareArgumentNode: node ].
self visitNode: aBlockNode body.
scope := scope popScope.
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitMethodNode: aMethodNode [
scope := compilationContext scope newMethodScope.
aMethodNode scope: scope. scope node: aMethodNode.
aMethodNode arguments do: [:node | self declareArgumentNode: node ].
aMethodNode pragmas do: [:each | self visitNode: each].
self visitNode: aMethodNode body.
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitPragmaNode: aPragmaNode [
super visitPragmaNode: aPragmaNode.
"we parse compiler options here so the rest of the compilation can react to them"
(aPragmaNode selector = #compilerOptions:)
ifTrue: [ aPragmaNode asPragma sendTo: aPragmaNode methodNode compilationContext ].
"primitives with a primitive error define a variable that can be referenced in the body"
aPragmaNode isPrimitiveError ifTrue: [
self declareVariableNode: (RBVariableNode named: (aPragmaNode argumentAt: #error:) value asString) ]
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitSequenceNode: aSequenceNode [
aSequenceNode temporaries do: [ :node | self declareVariableNode: node ].
aSequenceNode statements do: [ :each | self visitNode: each ].
aSequenceNode temporaries reverseDo: [ :node |
node binding isUnused
ifTrue: [ self unusedVariable: node ] ]
]
{ #category : #visiting }
OCASTSemanticAnalyzer >> visitVariableNode: aVariableNode [
| var |
var := (self lookupVariableForRead: aVariableNode)
ifNil: [(self undeclaredVariable: aVariableNode)].
aVariableNode binding: var.
var isUninitialized ifTrue: [self uninitializedVariable: aVariableNode].
]