-
Notifications
You must be signed in to change notification settings - Fork 715
/
c-writer.cc
6040 lines (5144 loc) · 185 KB
/
c-writer.cc
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright 2017 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wabt/c-writer.h"
#include <cctype>
#include <cinttypes>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <string_view>
#include <vector>
#include "wabt/cast.h"
#include "wabt/common.h"
#include "wabt/ir.h"
#include "wabt/literal.h"
#include "wabt/sha256.h"
#include "wabt/stream.h"
#include "wabt/string-util.h"
#define INDENT_SIZE 2
#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()
// code to be inserted into the generated output
extern const char* s_header_top;
extern const char* s_header_bottom;
extern const char* s_source_includes;
extern const char* s_source_declarations;
extern const char* s_simd_source_declarations;
extern const char* s_atomicops_source_declarations;
namespace wabt {
namespace {
struct Label {
Label(LabelType label_type,
const std::string& name,
const TypeVector& sig,
size_t type_stack_size,
size_t try_catch_stack_size,
bool used = false)
: label_type(label_type),
name(name),
sig(sig),
type_stack_size(type_stack_size),
try_catch_stack_size(try_catch_stack_size),
used(used) {}
bool HasValue() const { return !sig.empty(); }
LabelType label_type;
const std::string& name;
const TypeVector& sig;
size_t type_stack_size;
size_t try_catch_stack_size;
bool used = false;
};
struct LocalName {
explicit LocalName(const std::string& name) : name(name) {}
const std::string& name;
};
struct ParamName : LocalName {
using LocalName::LocalName;
ParamName(const Var& var) : LocalName(var.name()) {}
};
struct LabelName : LocalName {
using LocalName::LocalName;
};
struct GlobalName {
GlobalName(ModuleFieldType type, const std::string& name)
: type(type), name(name) {}
ModuleFieldType type;
const std::string& name;
};
struct TagSymbol : GlobalName {
explicit TagSymbol(const std::string& name)
: GlobalName(ModuleFieldType::Tag, name) {}
};
struct ExternalRef : GlobalName {
using GlobalName::GlobalName;
};
struct WrapperRef : GlobalName {
explicit WrapperRef(const std::string& name)
: GlobalName(ModuleFieldType::Func, name) {}
};
struct TailCallRef : GlobalName {
explicit TailCallRef(const std::string& name)
: GlobalName(ModuleFieldType::Func, name) {}
};
struct ExternalInstancePtr : GlobalName {
using GlobalName::GlobalName;
};
struct ExternalInstanceRef : GlobalName {
using GlobalName::GlobalName;
};
struct GotoLabel {
explicit GotoLabel(const Var& var) : var(var) {}
const Var& var;
};
struct LabelDecl {
explicit LabelDecl(const std::string& name) : name(name) {}
std::string name;
};
struct GlobalInstanceVar {
explicit GlobalInstanceVar(const Var& var) : var(var) {}
const Var& var;
};
struct StackVar {
explicit StackVar(Index index, Type type = Type::Any)
: index(index), type(type) {}
Index index;
Type type;
};
struct TypeEnum {
explicit TypeEnum(Type type) : type(type) {}
Type type;
};
struct SignedType {
explicit SignedType(Type type) : type(type) {}
Type type;
};
struct TryCatchLabel {
TryCatchLabel(const std::string& name, size_t try_catch_stack_size)
: name(name), try_catch_stack_size(try_catch_stack_size), used(false) {}
std::string name;
size_t try_catch_stack_size;
bool used;
};
struct FuncTypeExpr {
const FuncType* func_type;
FuncTypeExpr(const FuncType* f) : func_type(f) {}
};
struct Newline {};
struct OpenBrace {};
struct CloseBrace {};
int GetShiftMask(Type type) {
// clang-format off
switch (type) {
case Type::I32: return 31;
case Type::I64: return 63;
default: WABT_UNREACHABLE; return 0;
}
// clang-format on
}
/*
* This function is the default behavior for name_to_output_file_index_. For
* single .c output, this function returns a vector filled with 0. For multiple
* .c outputs, this function sorts all non-imported functions in the module by
* their names, and then divides all non-imported functions into equal-sized
* buckets (# of non-imported functions / # of .c outputs) based on the sorting.
*/
static std::vector<size_t> default_name_to_output_file_index(
std::vector<Func*>::const_iterator func_begin,
std::vector<Func*>::const_iterator func_end,
size_t num_imports,
size_t num_streams) {
std::vector<size_t> result;
result.resize(std::distance(func_begin, func_end));
if (num_streams == 1) {
return result;
}
std::map<std::string, Index> sorted_functions;
size_t non_imported_funcs = result.size() - num_imports;
size_t bucket_size = non_imported_funcs / num_streams +
(non_imported_funcs % num_streams ? 1 : 0);
Index func_index = 0;
for (auto func = func_begin; func != func_end; func++) {
sorted_functions.insert({(*func)->name, func_index});
++func_index;
}
Index sorted_func_index = 0;
for (const auto& [func_name, index] : sorted_functions) {
bool is_import = index < num_imports;
if (!is_import) {
result.at(index) = sorted_func_index / bucket_size;
++sorted_func_index;
}
}
return result;
}
class CWriter {
public:
CWriter(std::vector<Stream*>&& c_streams,
Stream* h_stream,
Stream* h_impl_stream,
const char* header_name,
const char* header_impl_name,
const WriteCOptions& options)
: options_(options),
c_streams_(std::move(c_streams)),
h_stream_(h_stream),
h_impl_stream_(h_impl_stream),
header_name_(header_name),
header_impl_name_(header_impl_name) {
module_prefix_ = MangleModuleName(options_.module_name);
if (c_streams_.size() != 1 && options.name_to_output_file_index) {
name_to_output_file_index_ = options.name_to_output_file_index;
} else {
name_to_output_file_index_ = default_name_to_output_file_index;
}
}
Result WriteModule(const Module&);
private:
using SymbolSet = std::set<std::string>;
using SymbolMap = std::map<std::string, std::string>;
using StackTypePair = std::pair<Index, Type>;
using StackVarSymbolMap = std::map<StackTypePair, std::string>;
void WriteCHeader();
void WriteCSource();
size_t MarkTypeStack() const;
void ResetTypeStack(size_t mark);
Type StackType(Index) const;
void PushType(Type);
void PushTypes(const TypeVector&);
void DropTypes(size_t count);
void PushLabel(LabelType,
const std::string& name,
const FuncSignature&,
bool used = false);
const Label* FindLabel(const Var& var, bool mark_used = true);
bool IsTopLabelUsed() const;
void PopLabel();
static constexpr char MangleType(Type);
static constexpr char MangleField(ModuleFieldType);
static std::string MangleTypes(const TypeVector&);
static std::string Mangle(std::string_view name, bool double_underscores);
static std::string MangleName(std::string_view);
static std::string MangleModuleName(std::string_view);
static std::string ExportName(std::string_view module_name,
std::string_view export_name);
std::string ExportName(std::string_view export_name) const;
static std::string TailCallExportName(std::string_view module_name,
std::string_view export_name);
std::string TailCallExportName(std::string_view export_name) const;
std::string ModuleInstanceTypeName() const;
static std::string ModuleInstanceTypeName(std::string_view module_name);
void ClaimName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& c_name);
std::string FindUniqueName(SymbolSet& set,
std::string_view proposed_name) const;
std::string ClaimUniqueName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& proposed_c_name);
void DefineImportName(const Import* import,
std::string_view module_name,
std::string_view field_name);
void ReserveExportNames();
void ReserveExportName(std::string_view);
std::string DefineImportedModuleInstanceName(std::string_view name);
std::string DefineInstanceMemberName(ModuleFieldType, std::string_view);
std::string DefineGlobalScopeName(ModuleFieldType, std::string_view);
std::string DefineLocalScopeName(std::string_view name, bool is_label);
std::string DefineParamName(std::string_view);
std::string DefineLabelName(std::string_view);
std::string DefineStackVarName(Index, Type, std::string_view);
static void SerializeFuncType(const FuncType&, std::string&);
std::string GetGlobalName(ModuleFieldType, const std::string&) const;
std::string GetLocalName(const std::string&, bool is_label) const;
std::string GetTailCallRef(const std::string&) const;
void Indent(int size = INDENT_SIZE);
void Dedent(int size = INDENT_SIZE);
void NonIndented(std::function<void()> func);
void WriteIndent();
void WriteData(const char* src, size_t size);
void Writef(const char* format, ...);
template <typename T, typename U, typename... Args>
void Write(T&& t, U&& u, Args&&... args) {
Write(std::forward<T>(t));
Write(std::forward<U>(u));
Write(std::forward<Args>(args)...);
}
static const char* GetReferenceTypeName(const Type& type);
static const char* GetReferenceNullValue(const Type& type);
static const char* GetCTypeName(const Type& type);
const char* InternalSymbolScope() const;
enum class CWriterPhase {
Declarations,
Definitions,
};
void Write() {}
void Write(Newline);
void Write(OpenBrace);
void Write(CloseBrace);
void Write(uint64_t);
void Write(std::string_view);
void Write(const ParamName&);
void Write(const LabelName&);
void Write(const GlobalName&);
void Write(const TagSymbol&);
void Write(const ExternalRef&);
void Write(const WrapperRef&);
void Write(const TailCallRef&);
void Write(const ExternalInstancePtr&);
void Write(const ExternalInstanceRef&);
void Write(Type);
void Write(SignedType);
void Write(TypeEnum);
void Write(const GotoLabel&);
void Write(const LabelDecl&);
void Write(const GlobalInstanceVar&);
void Write(const StackVar&);
void Write(const TypeVector&);
void Write(const Const&);
void WriteInitExpr(const ExprList&);
void WriteInitExprTerminal(const Expr*);
std::string GenerateHeaderGuard() const;
void WriteSourceTop();
void WriteMultiCTop();
void WriteMultiCTopEmpty();
void DeclareStruct(const TypeVector&);
void WriteTailcalleeParamTypes();
void WriteMultivalueResultTypes();
void WriteTagTypes();
void WriteFuncTypeDecls();
void WriteFuncTypes();
void Write(const FuncTypeExpr&);
void WriteTagDecls();
void WriteTags();
void ComputeUniqueImports();
void BeginInstance();
void WriteImports();
void WriteTailCallWeakImports();
void WriteFuncDeclarations();
void WriteFuncDeclaration(const FuncDeclaration&, const std::string&);
void WriteTailCallFuncDeclaration(const std::string&);
void WriteImportFuncDeclaration(const FuncDeclaration&,
const std::string& module_name,
const std::string&);
void WriteFuncRefWrapper(const Func* func);
void WriteCallIndirectFuncDeclaration(const FuncDeclaration&,
const std::string&);
void ComputeSimdScope();
void WriteHeaderIncludes();
void WriteV128Decl();
void WriteModuleInstance();
void WriteGlobals();
void WriteGlobal(const Global&, const std::string&);
void WriteGlobalPtr(const Global&, const std::string&);
void WriteMemories();
void WriteMemory(const std::string&, const Memory& memory);
void WriteMemoryPtr(const std::string&, const Memory& memory);
void WriteTables();
void WriteTable(const std::string&, const wabt::Type&);
void WriteTablePtr(const std::string&, const Table&);
void WriteTableType(const wabt::Type&);
void WriteDataInstances();
void WriteElemInstances();
void WriteGlobalInitializers();
void WriteDataInitializerDecls();
void WriteDataInitializers();
void WriteElemInitializerDecls();
void WriteElemInitializers();
void WriteFuncRefWrappers();
void WriteElemTableInit(bool, const ElemSegment*, const Table*);
bool IsSingleUnsharedMemory();
void InstallSegueBase(Memory* memory, bool save_old_value);
void RestoreSegueBase();
void WriteExports(CWriterPhase);
void WriteTailCallExports(CWriterPhase);
void WriteInitDecl();
void WriteFreeDecl();
void WriteGetFuncTypeDecl();
void WriteInit();
void WriteFree();
void WriteGetFuncType();
void WriteInitInstanceImport();
void WriteImportProperties(CWriterPhase);
void WriteFuncs();
void BeginFunction(const Func&);
void FinishFunction(size_t);
void Write(const Func&);
void WriteTailCallee(const Func&);
void WriteParamsAndLocals();
void WriteParams(const std::vector<std::string>& index_to_name,
bool setjmp_safe = false);
void WriteParamSymbols(const std::vector<std::string>& index_to_name);
void WriteParamTypes(const FuncDeclaration& decl);
void WriteLocals(const std::vector<std::string>& index_to_name);
void WriteStackVarDeclarations();
void Write(const ExprList&);
void WriteTailCallAsserts(const FuncSignature&);
void WriteTailCallStack();
void WriteUnwindTryCatchStack(const Label*);
void FinishReturnCall();
void Spill(const TypeVector&);
void Unspill(const TypeVector&);
template <typename Vars, typename TypeOf, typename ToDo>
void WriteVarsByType(const Vars&, const TypeOf&, const ToDo&, bool);
template <typename sources>
void Spill(const TypeVector&, const sources& src);
template <typename targets>
void Unspill(const TypeVector&, const targets& tgt);
enum class AssignOp {
Disallowed,
Allowed,
};
void WriteSimpleUnaryExpr(Opcode, const char* op);
void WriteInfixBinaryExpr(Opcode,
const char* op,
AssignOp = AssignOp::Allowed);
void WritePrefixBinaryExpr(Opcode, const char* op);
void WriteSignedBinaryExpr(Opcode, const char* op);
void Write(const BinaryExpr&);
void Write(const CompareExpr&);
void Write(const ConvertExpr&);
void Write(const LoadExpr&);
void Write(const StoreExpr&);
void Write(const UnaryExpr&);
void Write(const TernaryExpr&);
void Write(const SimdLaneOpExpr&);
void Write(const SimdLoadLaneExpr&);
void Write(const SimdStoreLaneExpr&);
void Write(const SimdShuffleOpExpr&);
void Write(const LoadSplatExpr&);
void Write(const LoadZeroExpr&);
void Write(const Block&);
void Write(const AtomicLoadExpr& expr);
void Write(const AtomicStoreExpr& expr);
void Write(const AtomicRmwExpr& expr);
void Write(const AtomicRmwCmpxchgExpr& expr);
size_t BeginTry(const TryExpr& tryexpr);
void WriteTryCatch(const TryExpr& tryexpr);
void WriteTryDelegate(const TryExpr& tryexpr);
void Write(const Catch& c);
void WriteThrow();
void PushTryCatch(const std::string& name);
void PopTryCatch();
void PushFuncSection(std::string_view include_condition = "");
bool IsImport(const std::string& name) const;
const WriteCOptions& options_;
const Module* module_ = nullptr;
const Func* func_ = nullptr;
Stream* stream_ = nullptr;
std::vector<Stream*> c_streams_;
Stream* h_stream_ = nullptr;
Stream* h_impl_stream_ = nullptr;
std::string header_name_;
std::string header_impl_name_;
Result result_ = Result::Ok;
int indent_ = 0;
bool should_write_indent_next_ = false;
int consecutive_newline_count_ = 0;
SymbolMap global_sym_map_;
SymbolMap local_sym_map_;
SymbolMap import_module_sym_map_;
StackVarSymbolMap stack_var_sym_map_;
SymbolSet global_syms_;
SymbolSet local_syms_;
TypeVector type_stack_;
std::vector<Label> label_stack_;
std::vector<TryCatchLabel> try_catch_stack_;
std::string module_prefix_;
SymbolSet typevector_structs_;
std::vector<const Import*> unique_imports_;
SymbolSet import_module_set_; // modules that are imported from
SymbolSet import_func_module_set_; // modules that funcs are imported from
std::vector<std::pair<std::string, MemoryStream>> func_sections_;
SymbolSet func_includes_;
std::vector<std::string> unique_func_type_names_;
std::function<std::vector<size_t>(std::vector<Func*>::const_iterator,
std::vector<Func*>::const_iterator,
size_t,
size_t)>
name_to_output_file_index_;
bool simd_used_in_header_;
bool in_tail_callee_;
};
// TODO: if WABT begins supporting debug names for labels,
// will need to avoid conflict between a label named "$Bfunc" and
// the implicit func label
static constexpr char kImplicitFuncLabel[] = "$Bfunc";
// These should be greater than any ModuleFieldType (used for MangleField).
static constexpr char kParamSuffix =
'a' + static_cast<char>(ModuleFieldType::Tag) + 1;
static constexpr char kLabelSuffix = kParamSuffix + 1;
static constexpr char kGlobalSymbolPrefix[] = "w2c_";
static constexpr char kWrapperSymbolPrefix[] = "wrap_";
static constexpr char kLocalSymbolPrefix[] = "var_";
static constexpr char kAdminSymbolPrefix[] = "wasm2c_";
static constexpr char kTailCallSymbolPrefix[] = "wasm_tailcall_";
static constexpr char kTailCallFallbackPrefix[] = "wasm_fallback_";
static constexpr unsigned int kTailCallStackSize = 1024;
size_t CWriter::MarkTypeStack() const {
return type_stack_.size();
}
void CWriter::ResetTypeStack(size_t mark) {
assert(mark <= type_stack_.size());
type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
}
Type CWriter::StackType(Index index) const {
assert(index < type_stack_.size());
return *(type_stack_.rbegin() + index);
}
void CWriter::PushType(Type type) {
type_stack_.push_back(type);
}
void CWriter::PushTypes(const TypeVector& types) {
type_stack_.insert(type_stack_.end(), types.begin(), types.end());
}
void CWriter::DropTypes(size_t count) {
assert(count <= type_stack_.size());
type_stack_.erase(type_stack_.end() - count, type_stack_.end());
}
void CWriter::PushLabel(LabelType label_type,
const std::string& name,
const FuncSignature& sig,
bool used) {
if (label_type == LabelType::Loop)
label_stack_.emplace_back(label_type, name, sig.param_types,
type_stack_.size(), try_catch_stack_.size(),
used);
else
label_stack_.emplace_back(label_type, name, sig.result_types,
type_stack_.size(), try_catch_stack_.size(),
used);
}
const Label* CWriter::FindLabel(const Var& var, bool mark_used) {
Label* label = nullptr;
if (var.is_index()) {
// We've generated names for all labels, so we should only be using an
// index when branching to the implicit function label, which can't be
// named.
assert(var.index() + 1 == label_stack_.size());
label = &label_stack_[0];
} else {
assert(var.is_name());
for (Index i = label_stack_.size(); i > 0; --i) {
label = &label_stack_[i - 1];
if (label->name == var.name())
break;
}
}
assert(label);
if (mark_used) {
label->used = true;
}
return label;
}
bool CWriter::IsTopLabelUsed() const {
assert(!label_stack_.empty());
return label_stack_.back().used;
}
void CWriter::PopLabel() {
label_stack_.pop_back();
}
// static
constexpr char CWriter::MangleType(Type type) {
// clang-format off
switch (type) {
case Type::I32: return 'i';
case Type::I64: return 'j';
case Type::F32: return 'f';
case Type::F64: return 'd';
case Type::V128: return 'o';
case Type::FuncRef: return 'r';
case Type::ExternRef: return 'e';
default:
WABT_UNREACHABLE;
}
// clang-format on
}
// static
constexpr char CWriter::MangleField(ModuleFieldType type) {
assert(static_cast<std::underlying_type<ModuleFieldType>::type>(type) <
std::numeric_limits<char>::max());
return 'a' + static_cast<char>(type);
}
// remove risky characters for pasting into a C-style comment
static std::string SanitizeForComment(std::string_view str) {
std::string result;
for (const uint8_t ch : str) {
// escape control chars, DEL, >7-bit chars, trigraphs, and end of comment
if (ch < ' ' || ch > '~' || ch == '?' || ch == '/') {
result += "\\" + StringPrintf("%02X", ch);
} else {
result += ch;
}
}
return result;
}
// static
std::string CWriter::MangleTypes(const TypeVector& types) {
assert(types.size() >= 2);
std::string result = "wasm_multi_";
for (auto type : types) {
result += MangleType(type);
}
return result;
}
/* The C symbol for an export from this module. */
std::string CWriter::ExportName(std::string_view export_name) const {
return kGlobalSymbolPrefix + module_prefix_ + '_' + MangleName(export_name);
}
/* The C symbol for an export from an arbitrary module. */
// static
std::string CWriter::ExportName(std::string_view module_name,
std::string_view export_name) {
return kGlobalSymbolPrefix + MangleModuleName(module_name) + '_' +
MangleName(export_name);
}
/* The C symbol for a tail-callee export from this module. */
std::string CWriter::TailCallExportName(std::string_view export_name) const {
return kTailCallSymbolPrefix + ExportName(export_name);
}
/* The C symbol for a tail-callee export from an arbitrary module. */
// static
std::string CWriter::TailCallExportName(std::string_view module_name,
std::string_view export_name) {
return kTailCallSymbolPrefix + ExportName(module_name, export_name);
}
/* The type name of an instance of this module. */
std::string CWriter::ModuleInstanceTypeName() const {
return kGlobalSymbolPrefix + module_prefix_;
}
/* The type name of an instance of an arbitrary module. */
// static
std::string CWriter::ModuleInstanceTypeName(std::string_view module_name) {
return kGlobalSymbolPrefix + MangleModuleName(module_name);
}
/*
* Hardcoded "C"-locale versions of isalpha/isdigit/isalnum/isxdigit for use
* in CWriter::Mangle(). We don't use the standard isalpha/isdigit/isalnum
* because the caller might have changed the current locale.
*/
static bool internal_isalpha(uint8_t ch) {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
}
static bool internal_isdigit(uint8_t ch) {
return (ch >= '0' && ch <= '9');
}
static bool internal_isalnum(uint8_t ch) {
return internal_isalpha(ch) || internal_isdigit(ch);
}
static bool internal_ishexdigit(uint8_t ch) {
return internal_isdigit(ch) || (ch >= 'A' && ch <= 'F'); // capitals only
}
// static
std::string CWriter::Mangle(std::string_view name, bool double_underscores) {
/*
* Name mangling transforms arbitrary Wasm names into "safe" C names
* in a deterministic way. To avoid collisions, distinct Wasm names must be
* transformed into distinct C names.
*
* The rules implemented here are:
* 1) any hex digit ('A' through 'F') that follows the sequence "0x"
* is escaped
* 2) any underscore at the beginning, at the end, or following another
* underscore, is escaped
* 3) if double_underscores is set, underscores are replaced with
* two underscores.
* 4) otherwise, any alphanumeric character is kept as-is,
* and any other character is escaped
*
* "Escaped" means the character is represented with the sequence "0xAB",
* where A B are hex digits ('0'-'9' or 'A'-'F') representing the character's
* numeric value.
*
* Module names are mangled with double_underscores=true to prevent
* collisions between, e.g., a module "alfa" with export
* "bravo_charlie" vs. a module "alfa_bravo" with export "charlie".
*/
enum State { Any, Zero, ZeroX, ZeroXHexDigit } state{Any};
bool last_was_underscore = false;
std::string result;
auto append_escaped = [&](const uint8_t ch) {
result += "0x" + StringPrintf("%02X", ch);
last_was_underscore = false;
state = Any;
};
auto append_verbatim = [&](const uint8_t ch) {
result += ch;
last_was_underscore = (ch == '_');
};
for (auto it = name.begin(); it != name.end(); ++it) {
const uint8_t ch = *it;
switch (state) {
case Any:
state = (ch == '0') ? Zero : Any;
break;
case Zero:
state = (ch == 'x') ? ZeroX : Any;
break;
case ZeroX:
state = internal_ishexdigit(ch) ? ZeroXHexDigit : Any;
break;
case ZeroXHexDigit:
WABT_UNREACHABLE;
break;
}
/* rule 1 */
if (state == ZeroXHexDigit) {
append_escaped(ch);
continue;
}
/* rule 2 */
if ((ch == '_') && ((it == name.begin()) || (std::next(it) == name.end()) ||
last_was_underscore)) {
append_escaped(ch);
continue;
}
/* rule 3 */
if (double_underscores && ch == '_') {
append_verbatim(ch);
append_verbatim(ch);
continue;
}
/* rule 4 */
if (internal_isalnum(ch) || (ch == '_')) {
append_verbatim(ch);
} else {
append_escaped(ch);
}
}
return result;
}
// static
std::string CWriter::MangleName(std::string_view name) {
return Mangle(name, false);
}
// static
std::string CWriter::MangleModuleName(std::string_view name) {
return Mangle(name, true);
}
/*
* Allocate a C symbol (must be unused) in the SymbolSet,
* and a mapping from the Wasm name (tagged with
* the index space of the name) to that C symbol.
*/
void CWriter::ClaimName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& c_name) {
const std::string type_tagged_wasm_name =
std::string(wasm_name) + type_suffix;
[[maybe_unused]] bool success;
success = set.insert(c_name).second;
assert(success);
success = map.emplace(type_tagged_wasm_name, c_name).second;
assert(success);
}
/*
* Make a proposed C symbol unique in a given symbol set by appending
* an integer to the symbol if necessary.
*/
std::string CWriter::FindUniqueName(SymbolSet& set,
std::string_view proposed_name) const {
std::string unique{proposed_name};
if (set.find(unique) != set.end()) {
std::string base = unique + "_";
size_t count = 0;
do {
unique = base + std::to_string(count++);
} while (set.find(unique) != set.end());
}
return unique;
}
/*
* Find a unique C symbol in the symbol set and claim it (mapping the
* type-tagged Wasm name to it).
*/
std::string CWriter::ClaimUniqueName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& proposed_c_name) {
const std::string unique = FindUniqueName(set, proposed_c_name);
ClaimName(set, map, type_suffix, wasm_name, unique);
return unique;
}
std::string_view StripLeadingDollar(std::string_view name) {
assert(!name.empty());
assert(name.front() == '$');
name.remove_prefix(1);
return name;
}
void CWriter::DefineImportName(const Import* import,
std::string_view module,
std::string_view field_name) {
std::string name;
ModuleFieldType type{};
switch (import->kind()) {
case ExternalKind::Func:
type = ModuleFieldType::Func;
name = cast<FuncImport>(import)->func.name;
break;
case ExternalKind::Tag:
type = ModuleFieldType::Tag;
name = cast<TagImport>(import)->tag.name;
break;
case ExternalKind::Global:
type = ModuleFieldType::Global;
name = cast<GlobalImport>(import)->global.name;
break;
case ExternalKind::Memory:
type = ModuleFieldType::Memory;
name = cast<MemoryImport>(import)->memory.name;
break;
case ExternalKind::Table:
type = ModuleFieldType::Table;
name = cast<TableImport>(import)->table.name;
break;
}
import_module_sym_map_.emplace(name, import->module_name);
const std::string mangled = ExportName(module, field_name);
global_syms_.erase(mangled); // duplicate imports are allowed
ClaimName(global_syms_, global_sym_map_, MangleField(type), name, mangled);
}
/*
* Reserve a C symbol for the public name of a module's export. The
* format of these is "w2c_" + the module prefix + "_" + the mangled
* export name. Reserving the symbol prevents internal functions and
* other names from shadowing/overlapping the exports.
*/
void CWriter::ReserveExportName(std::string_view name) {
ClaimName(global_syms_, global_sym_map_, MangleField(ModuleFieldType::Export),
name, ExportName(name));
}
/*
* Names for functions, function types, tags, and segments are globally unique
* across modules (formatted the same as an export, as "w2c_" + module prefix +
* "_" + the name, made unique if necessary).
*/
std::string CWriter::DefineGlobalScopeName(ModuleFieldType type,
std::string_view name) {
return ClaimUniqueName(global_syms_, global_sym_map_, MangleField(type), name,
ExportName(StripLeadingDollar(name)));
}
std::string CWriter::GetGlobalName(ModuleFieldType type,
const std::string& name) const {
std::string mangled = name + MangleField(type);
assert(global_sym_map_.count(mangled) == 1);
return global_sym_map_.at(mangled);
}
/* Names for params, locals, and stack vars are formatted as "var_" + name. */
std::string CWriter::DefineLocalScopeName(std::string_view name,
bool is_label) {
return ClaimUniqueName(
local_syms_, local_sym_map_, is_label ? kLabelSuffix : kParamSuffix, name,
kLocalSymbolPrefix + MangleName(StripLeadingDollar(name)));
}
std::string CWriter::GetLocalName(const std::string& name,
bool is_label) const {
std::string mangled = name + (is_label ? kLabelSuffix : kParamSuffix);
assert(local_sym_map_.count(mangled) == 1);
return local_sym_map_.at(mangled);
}
std::string CWriter::GetTailCallRef(const std::string& name) const {
return kTailCallSymbolPrefix + GetGlobalName(ModuleFieldType::Func, name);
}
std::string CWriter::DefineParamName(std::string_view name) {
return DefineLocalScopeName(name, false);
}
std::string CWriter::DefineLabelName(std::string_view name) {
return DefineLocalScopeName(name, true);
}
std::string CWriter::DefineStackVarName(Index index,
Type type,
std::string_view name) {
std::string unique =
FindUniqueName(local_syms_, kLocalSymbolPrefix + MangleName(name));
StackTypePair stp = {index, type};