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
359
360
361
362
363
364
365
366
367
368
369
370
|
#----------------------------------------------------------------------------
# PROCEDURES
#----------------------------------------------------------------------------
# Map C types to the C expression that should be used to initialise them.
# Only the types listed here can be used as array element types.
array set init_values {
uint8_t {rand ()}
uint16_t {rand ()}
uint32_t {rand ()}
int8_t {rand ()}
int16_t {rand ()}
int32_t {rand ()}
float {((rand() & 0x1ff) - 0x100) * 0x1.0p-4f}
}
# Types which have a special comparison function
array set equal_fns {
float floats_eq_p
}
# Return the value of test parameter NAME, as given in the spec file.
# The first optional argument is the default value.
proc param { name args } {
global contents specname
if { [info exists contents($name)] } {
return $contents($name)
}
if { [llength $args] == 0 } {
puts stderr "$specname: missing parameter '$name'"
exit 1
}
return [lindex $args 0]
}
# Execute BODY with LINE set to every nonempty line in TEXT.
proc foreach_nonempty_line { line text body } {
upvar $line up_line
foreach up_line [split $text "\n"] {
set up_line [string trim $up_line]
if { $up_line != "" } {
uplevel 1 $body
}
}
}
# Interpret the given string as a list of arrays. Return a list
# of TYPE NAME DIMS triplets, where TYPE is the type, NAME is the
# array name, and DIMS is a list of dimensions
proc parse_arrays { text } {
global specname
set arrays [list]
foreach_nonempty_line line $text {
set dims [list]
set rest $line
if { [regexp {^(\w+)\s+(\w+)\s*(.*);$} $rest dummy type name rest] } {
while { $rest != ""
&& [regexp {^\[([^\[]*)\]\s*(.*)} $rest dummy dim rest] } {
lappend dims $dim
}
}
if { $rest != "" } {
puts stderr "$specname: unrecognised array definition '$line'"
exit 1
}
lappend arrays $type $name $dims
}
return $arrays
}
# Interpret the given string as a list of scalar initialisers. Return
# a list of TYPE NAME VALUE triplets, where TYPE is the type, NAME is the
# variable name, and VALUE is the initial value.
proc parse_scalars { text } {
global specname
set scalars [list]
foreach_nonempty_line line $text {
if { ![regexp {^(\w+(?:\s*\*)*)\s*(\w+)\s*=\s*(.*);$} $line \
dummy type name value] } {
puts stderr "$specname: unrecognised scalar definition '$line'"
exit 1
}
lappend scalars $type $name $value
}
return $scalars
}
# Compare two values A and B of type TYPE and evalute to true if they are
# different.
proc ne_p { type a b } {
global equal_fns
if { [info exists equal_fns($type)] } {
return "!$equal_fns($type) ($a, $b)"
} else {
return "$a != $b"
}
}
# Execute a "foreach VARS LIST BODY" loop in which the body expands
# the string in ARGS. Treat each string as a separate line of C source
# code and return the block of code that the foreach loop produces.
proc text_foreach { vars list args } {
foreach var $vars {
upvar $var up_$var
lappend upvars up_$var
}
set text [list]
foreach $upvars $list {
foreach arg $args {
lappend text [uplevel 1 [list subst $arg]]
}
}
return [join $text "\n"]
}
# Declare that NAME is an array with element type TYPE and dimensions DIMS.
proc array_decl { type name dims } {
set line "$type $name"
foreach dim $dims {
append line "\[[string trim $dim]\]"
}
return "$line;"
}
# Declare that NAME points an array with element type TYPE and dimensions DIMS.
proc pointer_decl { type name dims } {
set num_dims [llength $dims]
if { $num_dims == 0 } {
return "$type $name"
} elseif { $num_dims == 1 } {
return "$type *$name"
} else {
set text "$type (*$name)"
foreach dim [lrange $dims 1 end] {
append text "\[[string trim $dim]\]"
}
return $text
}
}
# Emit loop headers to iterate over array dimensions DIMS. Set TEXT
# to a list of for loops, INDENT to the indentation of the loop body,
# and SUFFIX to the series of index operations for the current element.
proc array_loops { dims text indent suffix } {
upvar text out_text indent out_indent suffix out_suffix
set index 0
set out_text [list]
set out_suffix ""
set out_indent " "
foreach dim $dims {
set var "i$index"
incr index
set dim [string trim $dim]
lappend out_text \
"${out_indent}for (size_t $var = 0; $var < $dim; $var++)"
append out_suffix "\[$var\]"
set out_indent "$out_indent "
}
}
# Initialize array NAME with random data. DIMS gives the dimensions of
# the array and TYPE is the element type.
proc array_init { type name dims } {
global init_values specname
if { ![info exists init_values($type)] } {
puts stderr "$specname: no random initialiser defined for type '$type'"
exit 1
}
array_loops $dims text indent suffix
lappend text "$indent$name$suffix = $init_values($type);"
return [join $text "\n"]
}
# Execute CODE if arrays A and B are different. DIMS gives the dimensions of
# the arrays and TYPE is their element type.
proc array_compare { type a b dims code } {
global init_values specname
array_loops $dims text indent suffix
lappend text "${indent}if ([ne_p $type $a$suffix $b$suffix])"
lappend text "${indent} $code"
return [join $text "\n"]
}
# Expand TEXT. If it is different from the contents of file NAME,
# replace NAME with the new text.
proc genfile { name text } {
global basename specname
set filename "autosrc/$basename/$name"
set contents [uplevel 1 [list subst $text]]
# Remove leading and trailing blank lines.
regsub {^\n*} $contents "" contents
regsub {\n*$} $contents "\n" contents
# Make sure there is at most one blank line between blocks of text.
regsub -all {\n\n\n+} $contents "\n\n" contents
# Make sure that code blocks don't start or end with a blank line.
regsub -all {\{\n\n+} $contents "{\n" contents
regsub -all {\n+\n\}} $contents "\n}" contents
set contents "/* Automatically generated from $specname. */\n\n$contents"
if { [catch { open $filename r } file]
|| ![string equal [read $file] $contents] } {
set file [open $filename w]
puts -nonewline $file $contents
close $file
}
}
#----------------------------------------------------------------------------
# MAIN SCRIPT
#----------------------------------------------------------------------------
# Parse the arguments.
if { [llength $argv] != 1 } {
puts stderr "Usage: <spec name>"
exit 1
}
set basename [lindex $argv 0]
# Read the spec file into an array.
set specname "spec/$basename.txt"
if { [catch { open $specname r } file] } {
puts stderr $file
exit 1
}
array set contents [read $file]
close $file
# Create any parts of the output directory that don't already exist.
file mkdir "autosrc/$basename"
# Read and parse spec elements.
set arrays [parse_arrays [param arrays {}]]
set inputs [parse_scalars [param inputs {}]]
set outputs [parse_scalars [param outputs {}]]
set scalars [concat $inputs $outputs]
#----------------------------------------------------------------------------
genfile test.h {
[text_foreach include [concat stdint.h stdlib.h [param includes {}]] \
{#include <$include>}]
#define COUNT [param count]
[text_foreach { type name } [array get equal_fns] \
{extern int $name ($type, $type);}]
[text_foreach group { base peak } \
{[text_foreach { type name dims } $arrays \
{extern [array_decl $type ${group}_$name $dims]}]} \
{[text_foreach { type name value } $outputs \
{extern $type ${group}_$name;}]} \
""]
extern void initialise (void);
extern void base_loop (void);
extern void peak_loop (void);
extern void preload (void);
extern const char *compare_loops (void);
extern int base_repeat_count;
}
#----------------------------------------------------------------------------
genfile compare.c {
#include "test.h"
#include <string.h>
[text_foreach group { base peak } \
{[text_foreach { type name dims } $arrays \
{[array_decl $type ${group}_$name $dims] __attribute__((aligned(32)))}]} \
{[text_foreach { type name value } $outputs \
{$type ${group}_$name;}]} \
""]
int base_repeat_count = [param repeat];
void __attribute__((noinline))
initialise (void)
{
[text_foreach { type name dims } $arrays \
{[array_init $type base_$name $dims]} \
{ memcpy (peak_$name, base_$name, sizeof (base_$name));}]
}
void
preload (void)
{
[text_foreach { type name dims } $arrays \
{ for (size_t i = 0; i < sizeof (peak_$name); i += 32)} \
{ __asm volatile ("pld \[%0\]" :: "r" ((uintptr_t) peak_$name + i));}]
}
const char *__attribute__((noinline))
compare_loops (void)
{
[text_foreach { type name dims } $arrays \
{[array_compare $type base_$name peak_$name $dims "return \"$name\";"]}]
[text_foreach { type name value } $outputs \
{ if ([ne_p $type base_$name peak_$name])} \
{ return "$name"; }]
return 0;
}
}
#----------------------------------------------------------------------------
genfile base.c {
#include "test.h"
void __attribute__((noinline))
base_loop (void)
{
[text_foreach { type name dims } $arrays \
{ [pointer_decl $type $name $dims] = base_$name;}]
[text_foreach { type name value } $scalars \
{ $type $name = $value;}]
[param loop]
[text_foreach { type name value } $outputs \
{ base_$name = $name;}]
}
}
#----------------------------------------------------------------------------
genfile peak.c {
#include "test.h"
[text_foreach { type name dims } $arrays \
{static [pointer_decl $type "volatile vol_$name" $dims] = peak_$name;}]
[text_foreach { type name dims } $arrays \
{#define $name peak_$name}]
[text_foreach { type name value } $inputs \
{static $type volatile vol_$name = $value;}]
[text_foreach { type name dims } $arrays \
{#undef $name}]
void __attribute__((noinline))
peak_loop (void)
{
[text_foreach { type name dims } $arrays \
{ [pointer_decl $type "__restrict $name" $dims] = vol_$name;}]
[text_foreach { type name value } $inputs \
{ $type $name = vol_$name;}]
[text_foreach { type name value } $outputs \
{ $type $name = $value;}]
[param loop]
[text_foreach { type name value } $outputs \
{ peak_$name = $name;}]
}
}
|