@@ -164,7 +164,7 @@ void __init riscv_fill_hwcap(void)
164
164
165
165
switch (* ext ) {
166
166
case 's' :
167
- /**
167
+ /*
168
168
* Workaround for invalid single-letter 's' & 'u'(QEMU).
169
169
* No need to set the bit in riscv_isa as 's' & 'u' are
170
170
* not valid ISA extensions. It works until multi-letter
@@ -181,53 +181,101 @@ void __init riscv_fill_hwcap(void)
181
181
case 'X' :
182
182
case 'z' :
183
183
case 'Z' :
184
+ /*
185
+ * Before attempting to parse the extension itself, we find its end.
186
+ * As multi-letter extensions must be split from other multi-letter
187
+ * extensions with an "_", the end of a multi-letter extension will
188
+ * either be the null character or the "_" at the start of the next
189
+ * multi-letter extension.
190
+ *
191
+ * Next, as the extensions version is currently ignored, we
192
+ * eliminate that portion. This is done by parsing backwards from
193
+ * the end of the extension, removing any numbers. This may be a
194
+ * major or minor number however, so the process is repeated if a
195
+ * minor number was found.
196
+ *
197
+ * ext_end is intended to represent the first character *after* the
198
+ * name portion of an extension, but will be decremented to the last
199
+ * character itself while eliminating the extensions version number.
200
+ * A simple re-increment solves this problem.
201
+ */
184
202
ext_long = true;
185
- /* Multi-letter extension must be delimited */
186
203
for (; * isa && * isa != '_' ; ++ isa )
187
204
if (unlikely (!isalnum (* isa )))
188
205
ext_err = true;
189
- /* Parse backwards */
206
+
190
207
ext_end = isa ;
191
208
if (unlikely (ext_err ))
192
209
break ;
210
+
193
211
if (!isdigit (ext_end [-1 ]))
194
212
break ;
195
- /* Skip the minor version */
213
+
196
214
while (isdigit (* -- ext_end ))
197
215
;
198
- if (tolower (ext_end [0 ]) != 'p'
199
- || !isdigit (ext_end [-1 ])) {
200
- /* Advance it to offset the pre-decrement */
216
+
217
+ if (tolower (ext_end [0 ]) != 'p' || !isdigit (ext_end [-1 ])) {
201
218
++ ext_end ;
202
219
break ;
203
220
}
204
- /* Skip the major version */
221
+
205
222
while (isdigit (* -- ext_end ))
206
223
;
224
+
207
225
++ ext_end ;
208
226
break ;
209
227
default :
228
+ /*
229
+ * Things are a little easier for single-letter extensions, as they
230
+ * are parsed forwards.
231
+ *
232
+ * After checking that our starting position is valid, we need to
233
+ * ensure that, when isa was incremented at the start of the loop,
234
+ * that it arrived at the start of the next extension.
235
+ *
236
+ * If we are already on a non-digit, there is nothing to do. Either
237
+ * we have a multi-letter extension's _, or the start of an
238
+ * extension.
239
+ *
240
+ * Otherwise we have found the current extension's major version
241
+ * number. Parse past it, and a subsequent p/minor version number
242
+ * if present. The `p` extension must not appear immediately after
243
+ * a number, so there is no fear of missing it.
244
+ *
245
+ */
210
246
if (unlikely (!isalpha (* ext ))) {
211
247
ext_err = true;
212
248
break ;
213
249
}
214
- /* Find next extension */
250
+
215
251
if (!isdigit (* isa ))
216
252
break ;
217
- /* Skip the minor version */
253
+
218
254
while (isdigit (* ++ isa ))
219
255
;
256
+
220
257
if (tolower (* isa ) != 'p' )
221
258
break ;
259
+
222
260
if (!isdigit (* ++ isa )) {
223
261
-- isa ;
224
262
break ;
225
263
}
226
- /* Skip the major version */
264
+
227
265
while (isdigit (* ++ isa ))
228
266
;
267
+
229
268
break ;
230
269
}
270
+
271
+ /*
272
+ * The parser expects that at the start of an iteration isa points to the
273
+ * character before the start of the next extension. This will not be the
274
+ * case if we have just parsed a single-letter extension and the next
275
+ * extension is not a multi-letter extension prefixed with an "_". It is
276
+ * also not the case at the end of the string, where it will point to the
277
+ * terminating null character.
278
+ */
231
279
if (* isa != '_' )
232
280
-- isa ;
233
281
0 commit comments