blob: c2d83058bace5cfd0d37bd5bcd3b4306d436c3b3 [file] [log] [blame]
Zonr Changc383a502010-10-12 01:52:08 +08001/*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
zonr6315f762010-10-05 15:35:14 +080017#include "slang_rs_backend.h"
18
zonr6315f762010-10-05 15:35:14 +080019#include <string>
Stephen Hinese639eb52010-11-08 19:27:20 -080020#include <vector>
zonr6315f762010-10-05 15:35:14 +080021
22#include "llvm/ADT/Twine.h"
23#include "llvm/ADT/StringExtras.h"
24
Stephen Hinese639eb52010-11-08 19:27:20 -080025#include "llvm/Constant.h"
26#include "llvm/Constants.h"
27#include "llvm/DerivedTypes.h"
28#include "llvm/Function.h"
29#include "llvm/Metadata.h"
30#include "llvm/Module.h"
31
32#include "llvm/Support/IRBuilder.h"
33
Zonr Chang592a9542010-10-07 20:03:58 +080034#include "slang_rs.h"
zonr6315f762010-10-05 15:35:14 +080035#include "slang_rs_context.h"
zonr6315f762010-10-05 15:35:14 +080036#include "slang_rs_export_func.h"
37#include "slang_rs_export_type.h"
Stephen Hinese639eb52010-11-08 19:27:20 -080038#include "slang_rs_export_var.h"
39#include "slang_rs_metadata.h"
Shih-wei Liao462aefd2010-06-04 15:32:04 -070040
Stephen Hinese639eb52010-11-08 19:27:20 -080041namespace slang {
Shih-wei Liao9ef2f782010-10-01 12:31:37 -070042
43RSBackend::RSBackend(RSContext *Context,
Stephen Hinese639eb52010-11-08 19:27:20 -080044 clang::Diagnostic *Diags,
Shih-wei Liao9ef2f782010-10-01 12:31:37 -070045 const clang::CodeGenOptions &CodeGenOpts,
46 const clang::TargetOptions &TargetOpts,
47 const PragmaList &Pragmas,
48 llvm::raw_ostream *OS,
Zonr Chang3a9ca1f2010-10-06 17:52:56 +080049 Slang::OutputType OT,
Shih-wei Liao9ef2f782010-10-01 12:31:37 -070050 clang::SourceManager &SourceMgr,
zonr6315f762010-10-05 15:35:14 +080051 bool AllowRSPrefix)
52 : Backend(Diags,
53 CodeGenOpts,
54 TargetOpts,
55 Pragmas,
56 OS,
Zonr Chang3a9ca1f2010-10-06 17:52:56 +080057 OT),
zonr6315f762010-10-05 15:35:14 +080058 mContext(Context),
Zonr Chang3a9ca1f2010-10-06 17:52:56 +080059 mSourceMgr(SourceMgr),
60 mAllowRSPrefix(AllowRSPrefix),
zonr6315f762010-10-05 15:35:14 +080061 mExportVarMetadata(NULL),
62 mExportFuncMetadata(NULL),
63 mExportTypeMetadata(NULL) {
Shih-wei Liao9ef2f782010-10-01 12:31:37 -070064 return;
Shih-wei Liao462aefd2010-06-04 15:32:04 -070065}
66
Stephen Hinescfae0f32010-11-01 18:57:31 -070067// 1) Add zero initialization of local RS object types
68void RSBackend::AnnotateFunction(clang::FunctionDecl *FD) {
69 if (FD &&
70 FD->hasBody() &&
71 !SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr)) {
Stephen Hines1bdd4972010-11-08 17:35:08 -080072 mRefCount.Init(mContext->getASTContext());
Stephen Hines4b32ffd2010-11-05 18:47:11 -070073 mRefCount.Visit(FD->getBody());
Stephen Hinescfae0f32010-11-01 18:57:31 -070074 }
75 return;
76}
77
78void RSBackend::HandleTopLevelDecl(clang::DeclGroupRef D) {
79 // Disallow user-defined functions with prefix "rs"
80 if (!mAllowRSPrefix) {
81 // Iterate all function declarations in the program.
82 for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end();
83 I != E; I++) {
84 clang::FunctionDecl *FD = dyn_cast<clang::FunctionDecl>(*I);
85 if (FD == NULL)
86 continue;
87 if (!FD->getName().startswith("rs")) // Check prefix
88 continue;
89 if (!SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr))
90 mDiags.Report(clang::FullSourceLoc(FD->getLocation(), mSourceMgr),
91 mDiags.getCustomDiagID(clang::Diagnostic::Error,
92 "invalid function name prefix, "
93 "\"rs\" is reserved: '%0'"))
94 << FD->getName();
95 }
96 }
97
98 // Process any non-static function declarations
99 for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; I++) {
100 AnnotateFunction(dyn_cast<clang::FunctionDecl>(*I));
101 }
102
103 Backend::HandleTopLevelDecl(D);
104 return;
105}
106
Stephen Hinesfcda2352010-10-19 16:49:32 -0700107void RSBackend::HandleTranslationUnitPre(clang::ASTContext& C) {
Stephen Hinesfcda2352010-10-19 16:49:32 -0700108 clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
109
Stephen Hinescfae0f32010-11-01 18:57:31 -0700110 // Process any static function declarations
Stephen Hinesfcda2352010-10-19 16:49:32 -0700111 for (clang::DeclContext::decl_iterator I = TUDecl->decls_begin(),
112 E = TUDecl->decls_end(); I != E; I++) {
113 if ((I->getKind() >= clang::Decl::firstFunction) &&
114 (I->getKind() <= clang::Decl::lastFunction)) {
Stephen Hinescfae0f32010-11-01 18:57:31 -0700115 AnnotateFunction(static_cast<clang::FunctionDecl*>(*I));
Stephen Hinesfcda2352010-10-19 16:49:32 -0700116 }
117 }
118
119 return;
120}
121
122///////////////////////////////////////////////////////////////////////////////
Zonr Chang68fc02c2010-10-13 19:09:19 +0800123void RSBackend::HandleTranslationUnitPost(llvm::Module *M) {
Stephen Hinesc808a992010-11-29 17:20:42 -0800124 if (!mContext->processExport()) {
125 mDiags.Report(mDiags.getCustomDiagID(clang::Diagnostic::Error,
126 "elements cannot be exported"));
127 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700128
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700129 // Dump export variable info
130 if (mContext->hasExportVar()) {
131 if (mExportVarMetadata == NULL)
Zonr Chang68fc02c2010-10-13 19:09:19 +0800132 mExportVarMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_VAR_MN);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700133
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700134 llvm::SmallVector<llvm::Value*, 2> ExportVarInfo;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700135
zonr6315f762010-10-05 15:35:14 +0800136 for (RSContext::const_export_var_iterator I = mContext->export_vars_begin(),
137 E = mContext->export_vars_end();
138 I != E;
139 I++) {
140 const RSExportVar *EV = *I;
141 const RSExportType *ET = EV->getType();
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700142
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700143 // Variable name
144 ExportVarInfo.push_back(
145 llvm::MDString::get(mLLVMContext, EV->getName().c_str()));
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700146
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700147 // Type name
Zonr Changa65ec162010-10-17 01:53:05 +0800148 switch (ET->getClass()) {
149 case RSExportType::ExportClassPrimitive: {
150 ExportVarInfo.push_back(
151 llvm::MDString::get(
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700152 mLLVMContext, llvm::utostr_32(
Zonr Changa65ec162010-10-17 01:53:05 +0800153 static_cast<const RSExportPrimitiveType*>(ET)->getType())));
154 break;
155 }
156 case RSExportType::ExportClassPointer: {
157 ExportVarInfo.push_back(
158 llvm::MDString::get(
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700159 mLLVMContext, ("*" + static_cast<const RSExportPointerType*>(ET)
Zonr Changa65ec162010-10-17 01:53:05 +0800160 ->getPointeeType()->getName()).c_str()));
161 break;
162 }
163 case RSExportType::ExportClassMatrix: {
164 ExportVarInfo.push_back(
165 llvm::MDString::get(
166 mLLVMContext, llvm::utostr_32(
167 RSExportPrimitiveType::DataTypeRSMatrix2x2 +
168 static_cast<const RSExportMatrixType*>(ET)->getDim() - 2)));
169 break;
170 }
171 case RSExportType::ExportClassVector:
172 case RSExportType::ExportClassConstantArray:
173 case RSExportType::ExportClassRecord: {
174 ExportVarInfo.push_back(
175 llvm::MDString::get(mLLVMContext,
176 EV->getType()->getName().c_str()));
177 break;
178 }
179 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700180
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700181 mExportVarMetadata->addOperand(
182 llvm::MDNode::get(mLLVMContext,
183 ExportVarInfo.data(),
184 ExportVarInfo.size()) );
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700185
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700186 ExportVarInfo.clear();
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700187 }
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700188 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700189
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700190 // Dump export function info
191 if (mContext->hasExportFunc()) {
192 if (mExportFuncMetadata == NULL)
193 mExportFuncMetadata =
Zonr Chang68fc02c2010-10-13 19:09:19 +0800194 M->getOrInsertNamedMetadata(RS_EXPORT_FUNC_MN);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700195
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700196 llvm::SmallVector<llvm::Value*, 1> ExportFuncInfo;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700197
zonr6315f762010-10-05 15:35:14 +0800198 for (RSContext::const_export_func_iterator
199 I = mContext->export_funcs_begin(),
200 E = mContext->export_funcs_end();
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700201 I != E;
202 I++) {
zonr6315f762010-10-05 15:35:14 +0800203 const RSExportFunc *EF = *I;
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700204
205 // Function name
zonr6315f762010-10-05 15:35:14 +0800206 if (!EF->hasParam()) {
207 ExportFuncInfo.push_back(llvm::MDString::get(mLLVMContext,
208 EF->getName().c_str()));
209 } else {
Zonr Chang68fc02c2010-10-13 19:09:19 +0800210 llvm::Function *F = M->getFunction(EF->getName());
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700211 llvm::Function *HelperFunction;
212 const std::string HelperFunctionName(".helper_" + EF->getName());
213
214 assert(F && "Function marked as exported disappeared in Bitcode");
215
216 // Create helper function
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700217 {
Zonr Chang0da0a7d2010-10-05 21:26:37 +0800218 llvm::StructType *HelperFunctionParameterTy = NULL;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700219
Zonr Chang0da0a7d2010-10-05 21:26:37 +0800220 if (!F->getArgumentList().empty()) {
221 std::vector<const llvm::Type*> HelperFunctionParameterTys;
222 for (llvm::Function::arg_iterator AI = F->arg_begin(),
223 AE = F->arg_end(); AI != AE; AI++)
224 HelperFunctionParameterTys.push_back(AI->getType());
225
226 HelperFunctionParameterTy =
227 llvm::StructType::get(mLLVMContext, HelperFunctionParameterTys);
228 }
229
230 if (!EF->checkParameterPacketType(HelperFunctionParameterTy)) {
231 fprintf(stderr, "Failed to export function %s: parameter type "
232 "mismatch during creation of helper function.\n",
233 EF->getName().c_str());
234
235 const RSExportRecordType *Expected = EF->getParamPacketType();
236 if (Expected) {
237 fprintf(stderr, "Expected:\n");
238 Expected->getLLVMType()->dump();
239 }
240 if (HelperFunctionParameterTy) {
241 fprintf(stderr, "Got:\n");
242 HelperFunctionParameterTy->dump();
243 }
244 }
245
246 std::vector<const llvm::Type*> Params;
247 if (HelperFunctionParameterTy) {
248 llvm::PointerType *HelperFunctionParameterTyP =
249 llvm::PointerType::getUnqual(HelperFunctionParameterTy);
250 Params.push_back(HelperFunctionParameterTyP);
251 }
252
253 llvm::FunctionType * HelperFunctionType =
254 llvm::FunctionType::get(F->getReturnType(),
255 Params,
256 /* IsVarArgs = */false);
Shih-wei Liaocecd11d2010-09-21 08:07:58 -0700257
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700258 HelperFunction =
259 llvm::Function::Create(HelperFunctionType,
260 llvm::GlobalValue::ExternalLinkage,
261 HelperFunctionName,
Zonr Chang68fc02c2010-10-13 19:09:19 +0800262 M);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700263
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700264 HelperFunction->addFnAttr(llvm::Attribute::NoInline);
265 HelperFunction->setCallingConv(F->getCallingConv());
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700266
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700267 // Create helper function body
268 {
269 llvm::Argument *HelperFunctionParameter =
270 &(*HelperFunction->arg_begin());
271 llvm::BasicBlock *BB =
272 llvm::BasicBlock::Create(mLLVMContext, "entry", HelperFunction);
273 llvm::IRBuilder<> *IB = new llvm::IRBuilder<>(BB);
274 llvm::SmallVector<llvm::Value*, 6> Params;
275 llvm::Value *Idx[2];
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700276
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700277 Idx[0] =
278 llvm::ConstantInt::get(llvm::Type::getInt32Ty(mLLVMContext), 0);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700279
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700280 // getelementptr and load instruction for all elements in
281 // parameter .p
Zonr Chang0da0a7d2010-10-05 21:26:37 +0800282 for (size_t i = 0; i < EF->getNumParameters(); i++) {
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700283 // getelementptr
284 Idx[1] =
285 llvm::ConstantInt::get(
286 llvm::Type::getInt32Ty(mLLVMContext), i);
287 llvm::Value *Ptr = IB->CreateInBoundsGEP(HelperFunctionParameter,
288 Idx,
289 Idx + 2);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700290
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700291 // load
292 llvm::Value *V = IB->CreateLoad(Ptr);
293 Params.push_back(V);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700294 }
295
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700296 // Call and pass the all elements as paramter to F
297 llvm::CallInst *CI = IB->CreateCall(F,
298 Params.data(),
299 Params.data() + Params.size());
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700300
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700301 CI->setCallingConv(F->getCallingConv());
302
303 if (F->getReturnType() == llvm::Type::getVoidTy(mLLVMContext))
304 IB->CreateRetVoid();
305 else
306 IB->CreateRet(CI);
307
308 delete IB;
309 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700310 }
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700311
312 ExportFuncInfo.push_back(
313 llvm::MDString::get(mLLVMContext, HelperFunctionName.c_str()));
314 }
315
316 mExportFuncMetadata->addOperand(
317 llvm::MDNode::get(mLLVMContext,
318 ExportFuncInfo.data(),
319 ExportFuncInfo.size()));
320
321 ExportFuncInfo.clear();
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700322 }
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700323 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700324
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700325 // Dump export type info
326 if (mContext->hasExportType()) {
327 llvm::SmallVector<llvm::Value*, 1> ExportTypeInfo;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700328
zonr6315f762010-10-05 15:35:14 +0800329 for (RSContext::const_export_type_iterator
330 I = mContext->export_types_begin(),
331 E = mContext->export_types_end();
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700332 I != E;
333 I++) {
334 // First, dump type name list to export
335 const RSExportType *ET = I->getValue();
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700336
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700337 ExportTypeInfo.clear();
338 // Type name
339 ExportTypeInfo.push_back(
340 llvm::MDString::get(mLLVMContext, ET->getName().c_str()));
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700341
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700342 if (ET->getClass() == RSExportType::ExportClassRecord) {
343 const RSExportRecordType *ERT =
344 static_cast<const RSExportRecordType*>(ET);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700345
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700346 if (mExportTypeMetadata == NULL)
347 mExportTypeMetadata =
Zonr Chang68fc02c2010-10-13 19:09:19 +0800348 M->getOrInsertNamedMetadata(RS_EXPORT_TYPE_MN);
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700349
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700350 mExportTypeMetadata->addOperand(
351 llvm::MDNode::get(mLLVMContext,
352 ExportTypeInfo.data(),
353 ExportTypeInfo.size()));
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700354
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700355 // Now, export struct field information to %[struct name]
356 std::string StructInfoMetadataName("%");
357 StructInfoMetadataName.append(ET->getName());
358 llvm::NamedMDNode *StructInfoMetadata =
Zonr Chang68fc02c2010-10-13 19:09:19 +0800359 M->getOrInsertNamedMetadata(StructInfoMetadataName);
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700360 llvm::SmallVector<llvm::Value*, 3> FieldInfo;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700361
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700362 assert(StructInfoMetadata->getNumOperands() == 0 &&
363 "Metadata with same name was created before");
zonr6315f762010-10-05 15:35:14 +0800364 for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
365 FE = ERT->fields_end();
366 FI != FE;
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700367 FI++) {
368 const RSExportRecordType::Field *F = *FI;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700369
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700370 // 1. field name
371 FieldInfo.push_back(llvm::MDString::get(mLLVMContext,
372 F->getName().c_str()));
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700373
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700374 // 2. field type name
375 FieldInfo.push_back(
376 llvm::MDString::get(mLLVMContext,
377 F->getType()->getName().c_str()));
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700378
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700379 // 3. field kind
380 switch (F->getType()->getClass()) {
381 case RSExportType::ExportClassPrimitive:
382 case RSExportType::ExportClassVector: {
383 const RSExportPrimitiveType *EPT =
384 static_cast<const RSExportPrimitiveType*>(F->getType());
385 FieldInfo.push_back(
386 llvm::MDString::get(mLLVMContext,
387 llvm::itostr(EPT->getKind())));
388 break;
389 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700390
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700391 default: {
392 FieldInfo.push_back(
393 llvm::MDString::get(mLLVMContext,
394 llvm::itostr(
zonr6315f762010-10-05 15:35:14 +0800395 RSExportPrimitiveType::DataKindUser)));
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700396 break;
397 }
398 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700399
zonr6315f762010-10-05 15:35:14 +0800400 StructInfoMetadata->addOperand(llvm::MDNode::get(mLLVMContext,
401 FieldInfo.data(),
402 FieldInfo.size()));
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700403
404 FieldInfo.clear();
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700405 }
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700406 } // ET->getClass() == RSExportType::ExportClassRecord
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700407 }
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700408 }
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700409
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700410 return;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700411}
412
413RSBackend::~RSBackend() {
Shih-wei Liao9ef2f782010-10-01 12:31:37 -0700414 return;
Shih-wei Liao462aefd2010-06-04 15:32:04 -0700415}
Stephen Hinese639eb52010-11-08 19:27:20 -0800416
417} // namespace slang