-
Notifications
You must be signed in to change notification settings - Fork 67
/
CCodeGeneratorGlobalStructure.class.st
358 lines (299 loc) · 12.8 KB
/
CCodeGeneratorGlobalStructure.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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
"
This subclass of CCodeGenerator adds support for sticking most global variables into a large global array. This in turn means that suitably minded C compilers can do a better job of accessing those variables; in particular the Mac OS use of PPC and Acorn use of ARM benfits by a substantial margin.
Only simple globals are currently put in the array. Someday we might try adding pointers to the various arrays etc.
"
Class {
#name : #CCodeGeneratorGlobalStructure,
#superclass : #MLVMCCodeGenerator,
#instVars : [
'localStructDef',
'structDefDefine'
],
#category : #Melchor
}
{ #category : #'CAST translation' }
CCodeGeneratorGlobalStructure >> bindBlock: aTBlockNode withArgs: arguments [
| blockStatements argumentNames replacements |
blockStatements := aTBlockNode statements.
argumentNames := aTBlockNode arguments.
self assert: argumentNames size = arguments size.
replacements := argumentNames
with: arguments
collect: [ :name :value | name -> value ].
^ blockStatements collect: [ :anStatement |
anStatement copy bindVariableUsesIn: replacements asDictionary ]
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> buildSortedVariablesCollection [
"Build sorted vars, end result will be sorted collection based on static usage,
perhaps cache lines will like this!"
| globalNames |
globalNames := Bag new: globalVariableUsage size.
globalVariableUsage keysAndValuesDo:
[:k :v | | count |
count := 0.
v do:
[:methodName|
(methods at: methodName ifAbsent: []) ifNotNil:
[:method|
method parseTree nodesDo:
[:n|
(n isVariable
and: [n name hasEqualElements: k]) ifTrue:
[count := count + 1]]]].
globalNames
add: k "move arrays (e.g. methodCache) to end of struct"
withOccurrences: (((variableDeclarations at: k ifAbsent: ['']) includes: $[)
ifTrue: [count]
ifFalse: [count + 1000])].
variableDeclarations keysDo:
[:e | globalNames add: e withOccurrences: 0].
variables do:
[:e | globalNames add: e withOccurrences: 0].
^(globalNames sortedElements asSortedCollection:
[:a1 :a2| a1 value > a2 value or: [a1 value = a2 value and: [a1 key <= a2 key]]]) collect:
[:ea| ea key]
]
{ #category : #utilities }
CCodeGeneratorGlobalStructure >> checkForGlobalUsage: vars in: aTMethod [
"override to handle global struct needs"
super checkForGlobalUsage: vars in: aTMethod.
vars asSet do:
[:var |
"if any var is global and in the global var struct
tell the TMethod it may be refering to the struct, depending upon the #defines"
((variables includes: var)
and: [self placeInStructure: var]) ifTrue:
[aTMethod referencesGlobalStruct]]
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> emitCCodeOn: aStream doInlining: inlineFlag doAssertions: assertionFlag [
super emitCCodeOn: aStream doInlining: inlineFlag doAssertions: assertionFlag.
"we add an initialiser for the pointer to the global struct; "
aStream
cr;
nextPutAll: 'void initGlobalStructure(void) {';cr;
nextPutAll: '#if SQ_USE_GLOBAL_STRUCT_REG';cr;
nextPutAll: 'foo = &fum;' ; cr;
nextPutAll: '#endif'; cr;
nextPutAll:'}';
cr
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> emitCVariablesOn: aStream [
"Store the global variable declarations on the given stream.
Break logic into vars for structure and vars for non-structure."
| structure nonstruct |
structure := WriteStream on: (String new: 32768).
nonstruct := WriteStream on: (String new: 32768).
aStream nextPutAll: '/*** Variables ***/'; cr.
structure
nextPutAll: '#if SQ_USE_GLOBAL_STRUCT'; cr;
nextPutAll: '# define _iss /* define in-struct static as void */'; cr;
nextPutAll: 'static struct foo {'; cr;
nextPutAll: '#else'; cr;
nextPutAll: '# define _iss static'; cr;
nextPutAll: '#endif'; cr.
self buildSortedVariablesCollection do:
[ :var | | decl varString inStruct target |
target := (inStruct := self placeInStructure: (varString := var asString))
ifTrue: [structure]
ifFalse: [nonstruct].
decl := variableDeclarations at: varString ifAbsent: ['sqInt ' , varString].
(decl beginsWith: 'implicit') ifFalse: [
decl first == $# "support cgen var: #bytecodeSetSelector declareC: '#define bytecodeSetSelector 0' hack"
ifTrue:
[target nextPutAll: decl; cr]
ifFalse:
[self isGeneratingPluginCode
ifTrue:
[varString = 'interpreterProxy'
ifTrue: "quite special..."
[self preDeclareInterpreterProxyOn: target]
ifFalse: [target nextPutAll: 'static ']]
ifFalse:
[(self vmClass mustBeGlobal: varString) ifFalse:
[target nextPutAll: (inStruct ifTrue: ['_iss '] ifFalse: ['static '])]].
target nextPutAll: decl; nextPut: $;; cr]
]
].
structure
nextPutAll: '#undef _iss'; cr;
nextPutAll: '#if SQ_USE_GLOBAL_STRUCT'; cr;
nextPutAll: ' } fum;'; cr;
nextPutAll: ' #if SQ_USE_GLOBAL_STRUCT_REG';cr;
nextPutAll: '# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* using a global reg pointer */'; cr;
nextPutAll: '# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* using a global reg pointer */'; cr;
nextPutAll:'#else';cr;
nextPutAll: '# define DECL_MAYBE_SQ_GLOBAL_STRUCT register struct foo * foo = &fum;'; cr;
nextPutAll: '# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT volatile register struct foo * foo = &fum;'; cr;
nextPutAll: '#endif';cr;
nextPutAll: '# define GIV(interpreterInstVar) (foo->interpreterInstVar)'; cr;
nextPutAll: '#else'; cr;
nextPutAll: '# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* oh, no mr bill! */'; cr;
nextPutAll: '# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* oh no, mr bill! */'; cr;
nextPutAll: '# define GIV(interpreterInstVar) interpreterInstVar'; cr;
nextPutAll: '#endif'; cr.
"if the machine needs the fum structure defining locally, do it now; global register users don't need that, but DO need some batshit insane C macro fudging in order to convert the define of USE_GLOBAL_STRUCT_REG into a simple string to use in the asm clause below. Sigh."
structure
nextPutAll: '#if SQ_USE_GLOBAL_STRUCT'; cr;
nextPutAll: '#if SQ_USE_GLOBAL_STRUCT_REG';cr;
nextPutAll: '#define fooxstr(s) foostr(s)'; cr;
nextPutAll: '#define foostr(s) #s'; cr;
nextPutAll: 'register struct foo * foo asm(fooxstr(USE_GLOBAL_STRUCT_REG));'; cr;
nextPutAll: '#else'; cr;
nextPutAll: 'static struct foo * foo = &fum;'; cr;
nextPutAll: '#endif'; cr;
nextPutAll: '#endif'; cr.
aStream
nextPutAll: structure contents;
nextPutAll: nonstruct contents;
cr
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> emitGlobalStructFlagOn: aStream [
"Depending upon the value of structDefDefine (See also #structDefDefine:), define SQ_USE_GLOBAL_STRUCT before including the header. Also derive the flag for using the global register; define USE_GLOBAL_STRUCT_REG to do so"
aStream
nextPutAll: '#if ';
nextPutAll: structDefDefine; cr;
nextPutAll: '# define SQ_USE_GLOBAL_STRUCT 1'; cr;
nextPutAll: '#else'; cr;
nextPutAll: '# define SQ_USE_GLOBAL_STRUCT 0'; cr;
nextPutAll: '#endif'; cr;
nextPutAll: '#if USE_GLOBAL_STRUCT_REG '; cr;
nextPutAll: '# define SQ_USE_GLOBAL_STRUCT_REG 1'; cr;
nextPutAll: '#else'; cr;
nextPutAll: '# define SQ_USE_GLOBAL_STRUCT_REG 0'; cr;
nextPutAll: '#endif'; cr;
cr
]
{ #category : #'CAST translation' }
CCodeGeneratorGlobalStructure >> generateCASTSetFieldTo: aTSendNode [
| structType fieldName fieldVale setFieldStatements |
self assert: aTSendNode arguments size = 2.
fieldName := aTSendNode arguments first.
fieldVale := aTSendNode arguments second.
structType := self structTypeFor: aTSendNode receiver.
setFieldStatements := structType asClass allSlots collect: [ :slot |
| comparison |
comparison := TSendNode
receiver: (TSendNode
receiver: (TConstantNode value: slot name asString)
selector: 'strcmp:'
arguments: { fieldName })
selector: '='
arguments: { TConstantNode value: 0 }.
TSendNode
receiver: comparison
selector: 'ifTrue:'
arguments: {
TStatementListNode
statements: {
TSendNode
receiver: aTSendNode receiver
selector: (slot name, ':')
arguments: { fieldVale }
}
}
].
^ CCompoundStatementNode statements: (setFieldStatements collect: [ :e | e asCASTIn: self]).
]
{ #category : #'CAST translation' }
CCodeGeneratorGlobalStructure >> generateCASTWithFieldsDoSeparatedBy: aTSendNode [
| structType allRewrittenStatements blockSeparatorStatements fieldBlock allFieldArguments |
self assert: aTSendNode arguments size = 2.
self assert: aTSendNode arguments first arguments size = 2.
self assert: aTSendNode arguments second arguments size = 0.
fieldBlock := aTSendNode arguments first.
blockSeparatorStatements := aTSendNode arguments second statements.
structType := self structTypeFor: aTSendNode receiver.
allFieldArguments := structType asClass allSlots collect: [ :slot |
{
(TConstantNode value: slot name asString).
(TSendNode
receiver: aTSendNode receiver
selector: slot name
arguments: { }) } ].
allRewrittenStatements := OrderedCollection new.
allFieldArguments
do: [ :fieldArgs |
allRewrittenStatements addAll:
(self bindBlock: fieldBlock withArgs: fieldArgs) ]
separatedBy: [
allRewrittenStatements addAll: blockSeparatorStatements ].
^ CCompoundStatementNode statements:
(allRewrittenStatements collect: [ :e | e asCASTIn: self ])
]
{ #category : #'CAST translation' }
CCodeGeneratorGlobalStructure >> generateCASTWithStructNameDo: aTSendNode [
| argumentName structType rewrittenStatements aBlock |
self assert: aTSendNode arguments size = 1.
self assert: aTSendNode arguments first arguments size = 1.
structType := self structTypeFor: aTSendNode receiver.
aBlock := aTSendNode arguments first.
argumentName := aTSendNode arguments first arguments first.
rewrittenStatements := self
bindBlock: aBlock
withArgs:
{ (TConstantNode value: structType asString) }.
^ CCompoundStatementNode statements:
(rewrittenStatements collect: [ :e | e asCASTIn: self ])
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> initialize [
super initialize.
localStructDef := nil. "ignored ivar - no longer used"
structDefDefine := '1'
]
{ #category : #initialization }
CCodeGeneratorGlobalStructure >> initializeCASTTranslationDictionary [
super initializeCASTTranslationDictionary.
castTranslationDict at: #withStructNameDo: put: #generateCASTWithStructNameDo:.
castTranslationDict at: #withFieldsDo:separatedBy: put: #generateCASTWithFieldsDoSeparatedBy:.
castTranslationDict at: #setField:to: put: #generateCASTSetFieldTo:.
]
{ #category : #public }
CCodeGeneratorGlobalStructure >> isGlobalStructureBuild [
^true
]
{ #category : #utilities }
CCodeGeneratorGlobalStructure >> localizeGlobalVariables [
"TPR - remove all the global vars destined for the structure that are only used once - not worth the space,
actually what will happen is the folding code will fold these variables into the method"
super localizeGlobalVariables.
globalVariableUsage := globalVariableUsage select: [:e | e size > 1].
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> placeInStructure: var [
"See if we should put this array into a structure.
The variables listed are hardcoded as C in the interpreter thus they don't get resolved via TVariableNode logic.
Also let's ignore variables that have special definitions that require initialization, and the function def which has problems."
| check |
check := variableDeclarations at: var ifAbsent: [''].
(check includes: $=) ifTrue: [^false].
(check includes: $() ifTrue: [^false].
(check includesSubstring: 'static') ifTrue: [^false].
(check includesSubstring: 'volatile') ifTrue: [^false].
^(self vmClass mustBeGlobal: var) not
]
{ #category : #'C code generator' }
CCodeGeneratorGlobalStructure >> returnPrefixFromVariable: aName [
^((variables includes: aName)
and: [self placeInStructure: aName])
ifTrue: ['GIV(',aName,')']
ifFalse: [aName]
]
{ #category : #'initialize-release' }
CCodeGeneratorGlobalStructure >> structDefDefine: aString [
"set the string that will appear in the C file to define whether or not to use the global struct; reasonable values would be:
'USE_GLOBAL_STRUCT' - which would be defined in a header or makefile
'0' - which would mean never do it
'1' - which would mean always do it"
structDefDefine := aString
]
{ #category : #'CAST translation' }
CCodeGeneratorGlobalStructure >> structTypeFor: structNode [
| nodeType |
nodeType := self typeFor: structNode in: self currentMethod.
^ nodeType trimRight: [ :c | { $* . $ } includes: c ]
]