Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/compiler/compilerDirectives.cpp
40930 views
1
/*
2
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*
23
*/
24
25
#include "precompiled.hpp"
26
#include "ci/ciMethod.hpp"
27
#include "ci/ciUtilities.inline.hpp"
28
#include "compiler/abstractCompiler.hpp"
29
#include "compiler/compilerDirectives.hpp"
30
#include "compiler/compilerOracle.hpp"
31
#include "memory/allocation.inline.hpp"
32
#include "memory/resourceArea.hpp"
33
#include "runtime/globals_extension.hpp"
34
35
CompilerDirectives::CompilerDirectives() : _next(NULL), _match(NULL), _ref_count(0) {
36
_c1_store = new DirectiveSet(this);
37
_c1_store->init_control_intrinsic();
38
_c2_store = new DirectiveSet(this);
39
_c2_store->init_control_intrinsic();
40
};
41
42
CompilerDirectives::~CompilerDirectives() {
43
if (_c1_store != NULL) {
44
delete _c1_store;
45
}
46
if (_c2_store != NULL) {
47
delete _c2_store;
48
}
49
50
// remove all linked method matchers
51
BasicMatcher* tmp = _match;
52
while (tmp != NULL) {
53
BasicMatcher* next = tmp->next();
54
delete tmp;
55
tmp = next;
56
}
57
}
58
59
void CompilerDirectives::print(outputStream* st) {
60
assert(DirectivesStack_lock->owned_by_self(), "");
61
if (_match != NULL) {
62
st->cr();
63
st->print("Directive:");
64
if (is_default_directive()) {
65
st->print_cr(" (default)");
66
} else {
67
st->cr();
68
}
69
st->print(" matching: ");
70
_match->print(st);
71
BasicMatcher* tmp = _match->next();
72
while (tmp != NULL) {
73
st->print(", ");
74
tmp->print(st);
75
tmp = tmp->next();
76
}
77
st->cr();
78
} else {
79
assert(0, "There should always be a match");
80
}
81
82
if (_c1_store != NULL) {
83
st->print_cr(" c1 directives:");
84
_c1_store->print(st);
85
}
86
if (_c2_store != NULL) {
87
st->cr();
88
st->print_cr(" c2 directives:");
89
_c2_store->print(st);
90
}
91
//---
92
}
93
94
void CompilerDirectives::finalize(outputStream* st) {
95
if (_c1_store != NULL) {
96
_c1_store->finalize(st);
97
}
98
if (_c2_store != NULL) {
99
_c2_store->finalize(st);
100
}
101
}
102
103
void DirectiveSet::finalize(outputStream* st) {
104
// Check LogOption and warn
105
if (LogOption && !LogCompilation) {
106
st->print_cr("Warning: +LogCompilation must be set to enable compilation logging from directives");
107
}
108
if (PrintAssemblyOption && FLAG_IS_DEFAULT(DebugNonSafepoints)) {
109
warning("printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output");
110
DebugNonSafepoints = true;
111
}
112
113
// if any flag has been modified - set directive as enabled
114
// unless it already has been explicitly set.
115
if (!_modified[EnableIndex]) {
116
if (_inlinematchers != NULL) {
117
EnableOption = true;
118
return;
119
}
120
int i;
121
for (i = 0; i < number_of_flags; i++) {
122
if (_modified[i]) {
123
EnableOption = true;
124
return;
125
}
126
}
127
}
128
}
129
130
CompilerDirectives* CompilerDirectives::next() {
131
return _next;
132
}
133
134
bool CompilerDirectives::match(const methodHandle& method) {
135
if (is_default_directive()) {
136
return true;
137
}
138
if (method == NULL) {
139
return false;
140
}
141
if (_match->match(method)) {
142
return true;
143
}
144
return false;
145
}
146
147
bool CompilerDirectives::add_match(char* str, const char*& error_msg) {
148
BasicMatcher* bm = BasicMatcher::parse_method_pattern(str, error_msg, false);
149
if (bm == NULL) {
150
assert(error_msg != NULL, "Must have error message");
151
return false;
152
} else {
153
bm->set_next(_match);
154
_match = bm;
155
return true;
156
}
157
}
158
159
void CompilerDirectives::inc_refcount() {
160
assert(DirectivesStack_lock->owned_by_self(), "");
161
_ref_count++;
162
}
163
164
void CompilerDirectives::dec_refcount() {
165
assert(DirectivesStack_lock->owned_by_self(), "");
166
_ref_count--;
167
}
168
169
int CompilerDirectives::refcount() {
170
assert(DirectivesStack_lock->owned_by_self(), "");
171
return _ref_count;
172
}
173
174
DirectiveSet* CompilerDirectives::get_for(AbstractCompiler *comp) {
175
assert(DirectivesStack_lock->owned_by_self(), "");
176
if (comp == NULL) { // Xint
177
return _c1_store;
178
} else if (comp->is_c2()) {
179
return _c2_store;
180
} else {
181
// use c1_store as default
182
assert(comp->is_c1() || comp->is_jvmci(), "");
183
return _c1_store;
184
}
185
}
186
187
// In the list of Control/disabled intrinsics, the ID of the control intrinsics can separated:
188
// - by ',' (if -XX:Control/DisableIntrinsic is used once when invoking the VM) or
189
// - by '\n' (if -XX:Control/DisableIntrinsic is used multiple times when invoking the VM) or
190
// - by ' ' (if Control/DisableIntrinsic is used on a per-method level, e.g., with CompileCommand).
191
//
192
// To simplify the processing of the list, the canonicalize_control_intrinsic() method
193
// returns a new copy of the list in which '\n' and ' ' is replaced with ','.
194
ccstrlist DirectiveSet::canonicalize_control_intrinsic(ccstrlist option_value) {
195
char* canonicalized_list = NEW_C_HEAP_ARRAY(char, strlen(option_value) + 1, mtCompiler);
196
int i = 0;
197
char current;
198
while ((current = option_value[i]) != '\0') {
199
if (current == '\n' || current == ' ') {
200
canonicalized_list[i] = ',';
201
} else {
202
canonicalized_list[i] = current;
203
}
204
i++;
205
}
206
canonicalized_list[i] = '\0';
207
return canonicalized_list;
208
}
209
210
ControlIntrinsicIter::ControlIntrinsicIter(ccstrlist option_value, bool disable_all)
211
: _disableIntrinsic(disable_all) {
212
_list = (char*)DirectiveSet::canonicalize_control_intrinsic(option_value);
213
_saved_ptr = _list;
214
_enabled = false;
215
216
_token = strtok_r(_saved_ptr, ",", &_saved_ptr);
217
next_token();
218
}
219
220
ControlIntrinsicIter::~ControlIntrinsicIter() {
221
FREE_C_HEAP_ARRAY(char, _list);
222
}
223
224
// pre-increment
225
ControlIntrinsicIter& ControlIntrinsicIter::operator++() {
226
_token = strtok_r(NULL, ",", &_saved_ptr);
227
next_token();
228
return *this;
229
}
230
231
void ControlIntrinsicIter::next_token() {
232
if (_token && !_disableIntrinsic) {
233
char ch = _token[0];
234
235
if (ch != '+' && ch != '-') {
236
warning("failed to parse %s. must start with +/-!", _token);
237
} else {
238
_enabled = ch == '+';
239
_token++;
240
}
241
}
242
}
243
244
void DirectiveSet::init_control_intrinsic() {
245
for (ControlIntrinsicIter iter(ControlIntrinsic); *iter != NULL; ++iter) {
246
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
247
248
if (id != vmIntrinsics::_none) {
249
_intrinsic_control_words[vmIntrinsics::as_int(id)] = iter.is_enabled();
250
}
251
}
252
253
// Order matters, DisableIntrinsic can overwrite ControlIntrinsic
254
for (ControlIntrinsicIter iter(DisableIntrinsic, true/*disable_all*/); *iter != NULL; ++iter) {
255
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
256
257
if (id != vmIntrinsics::_none) {
258
_intrinsic_control_words[vmIntrinsics::as_int(id)] = false;
259
}
260
}
261
}
262
263
DirectiveSet::DirectiveSet(CompilerDirectives* d) :_inlinematchers(NULL), _directive(d) {
264
#define init_defaults_definition(name, type, dvalue, compiler) this->name##Option = dvalue;
265
compilerdirectives_common_flags(init_defaults_definition)
266
compilerdirectives_c2_flags(init_defaults_definition)
267
compilerdirectives_c1_flags(init_defaults_definition)
268
memset(_modified, 0, sizeof(_modified));
269
_intrinsic_control_words.fill_in(/*default value*/TriBool());
270
}
271
272
DirectiveSet::~DirectiveSet() {
273
// remove all linked methodmatchers
274
InlineMatcher* tmp = _inlinematchers;
275
while (tmp != NULL) {
276
InlineMatcher* next = tmp->next();
277
delete tmp;
278
tmp = next;
279
}
280
}
281
282
// A smart pointer of DirectiveSet. It uses Copy-on-Write strategy to avoid cloning.
283
// It provides 2 accesses of the underlying raw pointer.
284
// 1) operator->() returns a pointer to a constant DirectiveSet. It's read-only.
285
// 2) cloned() returns a pointer that points to the cloned DirectiveSet.
286
// Users should only use cloned() when they need to update DirectiveSet.
287
//
288
// In the end, users need to invoke commit() to finalize the pending changes.
289
// If cloning happens, the smart pointer will return the new pointer after releasing the original
290
// one on DirectivesStack. If cloning doesn't happen, it returns the original intact pointer.
291
class DirectiveSetPtr {
292
private:
293
DirectiveSet* _origin;
294
DirectiveSet* _clone;
295
NONCOPYABLE(DirectiveSetPtr);
296
297
public:
298
DirectiveSetPtr(DirectiveSet* origin): _origin(origin), _clone(nullptr) {
299
assert(origin != nullptr, "DirectiveSetPtr cannot be initialized with a NULL pointer.");
300
}
301
302
DirectiveSet const* operator->() {
303
return (_clone == nullptr) ? _origin : _clone;
304
}
305
306
DirectiveSet* cloned() {
307
if (_clone == nullptr) {
308
_clone = DirectiveSet::clone(_origin);
309
}
310
return _clone;
311
}
312
313
DirectiveSet* commit() {
314
if (_clone != nullptr) {
315
// We are returning a (parentless) copy. The originals parent don't need to account for this.
316
DirectivesStack::release(_origin);
317
_origin = _clone;
318
_clone = nullptr;
319
}
320
321
return _origin;
322
}
323
};
324
325
// Backward compatibility for CompileCommands
326
// Breaks the abstraction and causes lots of extra complexity
327
// - if some option is changed we need to copy directiveset since it no longer can be shared
328
// - Need to free copy after use
329
// - Requires a modified bit so we don't overwrite options that is set by directives
330
DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle& method) {
331
// Early bail out - checking all options is expensive - we rely on them not being used
332
// Only set a flag if it has not been modified and value changes.
333
// Only copy set if a flag needs to be set
334
if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::has_any_command_set()) {
335
DirectiveSetPtr set(this);
336
337
// All CompileCommands are not equal so this gets a bit verbose
338
// When CompileCommands have been refactored less clutter will remain.
339
if (CompilerOracle::should_break_at(method)) {
340
if (!_modified[BreakAtCompileIndex]) {
341
set.cloned()->BreakAtCompileOption = true;
342
}
343
if (!_modified[BreakAtExecuteIndex]) {
344
set.cloned()->BreakAtExecuteOption = true;
345
}
346
}
347
if (!_modified[LogIndex]) {
348
bool log = CompilerOracle::should_log(method);
349
if (log != set->LogOption) {
350
set.cloned()->LogOption = log;
351
}
352
}
353
354
if (CompilerOracle::should_print(method)) {
355
if (!_modified[PrintAssemblyIndex]) {
356
set.cloned()->PrintAssemblyOption = true;
357
}
358
}
359
// Exclude as in should not compile == Enabled
360
if (CompilerOracle::should_exclude(method)) {
361
if (!_modified[ExcludeIndex]) {
362
set.cloned()->ExcludeOption = true;
363
}
364
}
365
366
// inline and dontinline (including exclude) are implemented in the directiveset accessors
367
#define init_default_cc(name, type, dvalue, cc_flag) { type v; if (!_modified[name##Index] && CompileCommand::cc_flag != CompileCommand::Unknown && CompilerOracle::has_option_value(method, CompileCommand::cc_flag, v) && v != this->name##Option) { set.cloned()->name##Option = v; } }
368
compilerdirectives_common_flags(init_default_cc)
369
compilerdirectives_c2_flags(init_default_cc)
370
compilerdirectives_c1_flags(init_default_cc)
371
372
// Canonicalize DisableIntrinsic to contain only ',' as a separator.
373
ccstrlist option_value;
374
bool need_reset = true; // if Control/DisableIntrinsic redefined, only need to reset control_words once
375
376
if (!_modified[ControlIntrinsicIndex] &&
377
CompilerOracle::has_option_value(method, CompileCommand::ControlIntrinsic, option_value)) {
378
ControlIntrinsicIter iter(option_value);
379
380
if (need_reset) {
381
set.cloned()->_intrinsic_control_words.fill_in(TriBool());
382
need_reset = false;
383
}
384
385
while (*iter != NULL) {
386
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
387
if (id != vmIntrinsics::_none) {
388
set.cloned()->_intrinsic_control_words[vmIntrinsics::as_int(id)] = iter.is_enabled();
389
}
390
391
++iter;
392
}
393
}
394
395
396
if (!_modified[DisableIntrinsicIndex] &&
397
CompilerOracle::has_option_value(method, CompileCommand::DisableIntrinsic, option_value)) {
398
ControlIntrinsicIter iter(option_value, true/*disable_all*/);
399
400
if (need_reset) {
401
set.cloned()->_intrinsic_control_words.fill_in(TriBool());
402
need_reset = false;
403
}
404
405
while (*iter != NULL) {
406
vmIntrinsics::ID id = vmIntrinsics::find_id(*iter);
407
if (id != vmIntrinsics::_none) {
408
set.cloned()->_intrinsic_control_words[vmIntrinsics::as_int(id)] = false;
409
}
410
411
++iter;
412
}
413
}
414
415
return set.commit();
416
}
417
// Nothing changed
418
return this;
419
}
420
421
CompilerDirectives* DirectiveSet::directive() {
422
assert(_directive != NULL, "Must have been initialized");
423
return _directive;
424
}
425
426
bool DirectiveSet::matches_inline(const methodHandle& method, int inline_action) {
427
if (_inlinematchers != NULL) {
428
if (_inlinematchers->match(method, inline_action)) {
429
return true;
430
}
431
}
432
return false;
433
}
434
435
bool DirectiveSet::should_inline(ciMethod* inlinee) {
436
inlinee->check_is_loaded();
437
VM_ENTRY_MARK;
438
methodHandle mh(THREAD, inlinee->get_Method());
439
440
if (_inlinematchers != NULL) {
441
return matches_inline(mh, InlineMatcher::force_inline);
442
}
443
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
444
return CompilerOracle::should_inline(mh);
445
}
446
return false;
447
}
448
449
bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
450
inlinee->check_is_loaded();
451
VM_ENTRY_MARK;
452
methodHandle mh(THREAD, inlinee->get_Method());
453
454
if (_inlinematchers != NULL) {
455
return matches_inline(mh, InlineMatcher::dont_inline);
456
}
457
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
458
return CompilerOracle::should_not_inline(mh);
459
}
460
return false;
461
}
462
463
bool DirectiveSet::parse_and_add_inline(char* str, const char*& error_msg) {
464
InlineMatcher* m = InlineMatcher::parse_inline_pattern(str, error_msg);
465
if (m != NULL) {
466
// add matcher last in chain - the order is significant
467
append_inline(m);
468
return true;
469
} else {
470
assert(error_msg != NULL, "Error message must be set");
471
return false;
472
}
473
}
474
475
void DirectiveSet::append_inline(InlineMatcher* m) {
476
if (_inlinematchers == NULL) {
477
_inlinematchers = m;
478
return;
479
}
480
InlineMatcher* tmp = _inlinematchers;
481
while (tmp->next() != NULL) {
482
tmp = tmp->next();
483
}
484
tmp->set_next(m);
485
}
486
487
void DirectiveSet::print_inline(outputStream* st) {
488
if (_inlinematchers == NULL) {
489
st->print_cr(" inline: -");
490
} else {
491
st->print(" inline: ");
492
_inlinematchers->print(st);
493
InlineMatcher* tmp = _inlinematchers->next();
494
while (tmp != NULL) {
495
st->print(", ");
496
tmp->print(st);
497
tmp = tmp->next();
498
}
499
st->cr();
500
}
501
}
502
503
bool DirectiveSet::is_intrinsic_disabled(const methodHandle& method) {
504
vmIntrinsics::ID id = method->intrinsic_id();
505
assert(id > vmIntrinsics::_none && id < vmIntrinsics::ID_LIMIT, "invalid intrinsic_id!");
506
507
TriBool b = _intrinsic_control_words[vmIntrinsics::as_int(id)];
508
if (b.is_default()) {
509
return false; // if unset, every intrinsic is enabled.
510
} else {
511
return !b;
512
}
513
}
514
515
DirectiveSet* DirectiveSet::clone(DirectiveSet const* src) {
516
DirectiveSet* set = new DirectiveSet(NULL);
517
// Ordinary allocations of DirectiveSet would call init_control_intrinsic()
518
// immediately to create a new copy for set->Control/DisableIntrinsicOption.
519
// However, here it does not need to because the code below creates
520
// a copy of src->Control/DisableIntrinsicOption that initializes
521
// set->Control/DisableIntrinsicOption.
522
523
memcpy(set->_modified, src->_modified, sizeof(src->_modified));
524
525
InlineMatcher* tmp = src->_inlinematchers;
526
while (tmp != NULL) {
527
set->append_inline(tmp->clone());
528
tmp = tmp->next();
529
}
530
531
#define copy_members_definition(name, type, dvalue, cc_flag) set->name##Option = src->name##Option;
532
compilerdirectives_common_flags(copy_members_definition)
533
compilerdirectives_c2_flags(copy_members_definition)
534
compilerdirectives_c1_flags(copy_members_definition)
535
536
set->_intrinsic_control_words = src->_intrinsic_control_words;
537
return set;
538
}
539
540
// Create a new dirstack and push a default directive
541
void DirectivesStack::init() {
542
CompilerDirectives* _default_directives = new CompilerDirectives();
543
char str[] = "*.*";
544
const char* error_msg = NULL;
545
_default_directives->add_match(str, error_msg);
546
#if defined(COMPILER1) || INCLUDE_JVMCI
547
_default_directives->_c1_store->EnableOption = true;
548
#endif
549
#ifdef COMPILER2
550
if (CompilerConfig::is_c2_enabled()) {
551
_default_directives->_c2_store->EnableOption = true;
552
}
553
#endif
554
assert(error_msg == NULL, "Must succeed.");
555
push(_default_directives);
556
}
557
558
DirectiveSet* DirectivesStack::getDefaultDirective(AbstractCompiler* comp) {
559
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
560
561
assert(_bottom != NULL, "Must never be empty");
562
_bottom->inc_refcount();
563
return _bottom->get_for(comp);
564
}
565
566
void DirectivesStack::push(CompilerDirectives* directive) {
567
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
568
569
directive->inc_refcount();
570
if (_top == NULL) {
571
assert(_bottom == NULL, "There can only be one default directive");
572
_bottom = directive; // default directive, can never be removed.
573
}
574
575
directive->set_next(_top);
576
_top = directive;
577
_depth++;
578
}
579
580
void DirectivesStack::pop(int count) {
581
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
582
assert(count > -1, "No negative values");
583
for (int i = 0; i < count; i++) {
584
pop_inner();
585
}
586
}
587
588
void DirectivesStack::pop_inner() {
589
assert(DirectivesStack_lock->owned_by_self(), "");
590
591
if (_top->next() == NULL) {
592
return; // Do nothing - don't allow an empty stack
593
}
594
CompilerDirectives* tmp = _top;
595
_top = _top->next();
596
_depth--;
597
598
DirectivesStack::release(tmp);
599
}
600
601
bool DirectivesStack::check_capacity(int request_size, outputStream* st) {
602
if ((request_size + _depth) > CompilerDirectivesLimit) {
603
st->print_cr("Could not add %i more directives. Currently %i/%i directives.", request_size, _depth, CompilerDirectivesLimit);
604
return false;
605
}
606
return true;
607
}
608
609
void DirectivesStack::clear() {
610
// holding the lock during the whole operation ensuring consistent result
611
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
612
while (_top->next() != NULL) {
613
pop_inner();
614
}
615
}
616
617
void DirectivesStack::print(outputStream* st) {
618
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
619
CompilerDirectives* tmp = _top;
620
while (tmp != NULL) {
621
tmp->print(st);
622
tmp = tmp->next();
623
st->cr();
624
}
625
}
626
627
void DirectivesStack::release(DirectiveSet* set) {
628
assert(set != NULL, "Never NULL");
629
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
630
if (set->is_exclusive_copy()) {
631
// Old CompilecCmmands forced us to create an exclusive copy
632
delete set;
633
} else {
634
assert(set->directive() != NULL, "Never NULL");
635
release(set->directive());
636
}
637
}
638
639
640
void DirectivesStack::release(CompilerDirectives* dir) {
641
assert(DirectivesStack_lock->owned_by_self(), "");
642
dir->dec_refcount();
643
if (dir->refcount() == 0) {
644
delete dir;
645
}
646
}
647
648
DirectiveSet* DirectivesStack::getMatchingDirective(const methodHandle& method, AbstractCompiler *comp) {
649
assert(_depth > 0, "Must never be empty");
650
651
DirectiveSet* match = NULL;
652
{
653
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
654
655
CompilerDirectives* dir = _top;
656
assert(dir != NULL, "Must be initialized");
657
658
while (dir != NULL) {
659
if (dir->is_default_directive() || dir->match(method)) {
660
match = dir->get_for(comp);
661
assert(match != NULL, "Consistency");
662
if (match->EnableOption) {
663
// The directiveSet for this compile is also enabled -> success
664
dir->inc_refcount();
665
break;
666
}
667
}
668
dir = dir->next();
669
}
670
}
671
guarantee(match != NULL, "There should always be a default directive that matches");
672
673
// Check for legacy compile commands update, without DirectivesStack_lock
674
return match->compilecommand_compatibility_init(method);
675
}
676
677