2 #include <boost/foreach.hpp>
4 #include <ozulis/core/assert.hh>
5 #include <ozulis/ast/ast-cast.hh>
6 #include <ozulis/ast/cast-tables.hh>
7 #include <ozulis/visitors/browser.hh>
9 #include "llvm-asm-generator.hh"
15 LLVMAsmGenerator::LLVMAsmGenerator(std::ostream
& out
)
16 : ConstVisitor
<LLVMAsmGenerator
>(),
20 assert_msg(out
.good(), "The output for llvm assembly is not valid.");
21 out_
<< std::setprecision(1000);
23 #define ADD_CAST_TABLE_ENTRY(Type1, Type2) \
25 castTableKey_t key = std::make_pair(ast::Type1##Type::nodeTypeId(), \
26 ast::Type2##Type::nodeTypeId()); \
27 assert_msg(!castTable_.count(key), \
28 "the same key has been insterted two times"); \
29 castTable_[key] = &LLVMAsmGenerator::cast##Type1##To##Type2; \
32 ADD_CAST_TABLE_ENTRY(Bool
, Bool
);
33 ADD_CAST_TABLE_ENTRY(Bool
, Integer
);
34 ADD_CAST_TABLE_ENTRY(Bool
, Float
);
35 ADD_CAST_TABLE_ENTRY(Bool
, Double
);
36 ADD_CAST_TABLE_ENTRY(Integer
, Bool
);
37 ADD_CAST_TABLE_ENTRY(Integer
, Integer
);
38 ADD_CAST_TABLE_ENTRY(Integer
, Float
);
39 ADD_CAST_TABLE_ENTRY(Integer
, Double
);
40 ADD_CAST_TABLE_ENTRY(Float
, Double
);
41 ADD_CAST_TABLE_ENTRY(Float
, Bool
);
42 ADD_CAST_TABLE_ENTRY(Double
, Bool
);
44 ADD_CAST_TABLE_ENTRY(Integer
, Pointer
);
45 ADD_CAST_TABLE_ENTRY(Pointer
, Integer
);
48 LLVMAsmGenerator::~LLVMAsmGenerator()
53 LLVMAsmGenerator::writeFunctionHeader(const ast::FunctionDecl
& node
)
55 LLVMAsmGenerator::visit(*node
.returnType
, *this);
56 out_
<< " @" << node
.name
<< "(";
58 for (std::vector
<ast::VarDecl
*>::iterator it
= node
.args
->begin();
59 it
!= node
.args
->end(); it
++)
61 if (it
!= node
.args
->begin())
63 LLVMAsmGenerator::visit(*(*it
)->type
, *this);
64 out_
<< " %$" << (*it
)->name
;
67 out_
<< ")" << std::endl
;
70 static void visitFunctionDecl(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
72 const ast::FunctionDecl node
= reinterpret_cast<const ast::FunctionDecl
&> (node_
);
73 ctx
.out_
<< "declare ";
74 ctx
.writeFunctionHeader(node
);
77 static void visitFunction(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
79 const ast::Function node
= reinterpret_cast<const ast::Function
&> (node_
);
80 ctx
.out_
<< "define ";
81 ctx
.writeFunctionHeader(node
);
82 ctx
.out_
<< "{" << std::endl
;
84 BOOST_FOREACH (ast::VarDecl
* var
, *node
.args
)
85 LLVMAsmGenerator::visit(*var
, ctx
);
90 ast::MemoryAddress addr
;
94 BOOST_FOREACH (ast::VarDecl
* var
, *node
.args
)
98 symbol
.name
= "$" + var
->name
;
99 symbol
.type
= var
->type
;
100 symbol
.address
= &addr
;
101 LLVMAsmGenerator::visit(sv
, ctx
);
104 LLVMAsmGenerator::visit(*node
.block
, ctx
);
105 ctx
.out_
<< "}" << std::endl
;
108 static void visitVarDecl(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
110 const ast::VarDecl node
= reinterpret_cast<const ast::VarDecl
&> (node_
);
113 ctx
.out_
<< " %" << node
.name
<< " = alloca ";
114 LLVMAsmGenerator::visit(*node
.type
, ctx
);
115 ctx
.out_
<< std::endl
;
118 static void visitLoadVar(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
120 const ast::LoadVar node
= reinterpret_cast<const ast::LoadVar
&> (node_
);
121 assert(node
.from
->type
);
123 ctx
.out_
<< " %" << node
.to
<< " = load ";
124 LLVMAsmGenerator::visit(*node
.from
->type
, ctx
);
126 LLVMAsmGenerator::visit(*node
.from
, ctx
);
127 ctx
.out_
<< std::endl
;
130 static void visitStoreVar(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
132 const ast::StoreVar node
= reinterpret_cast<const ast::StoreVar
&> (node_
);
134 assert(node
.value
->type
);
136 ctx
.out_
<< " store ";
137 LLVMAsmGenerator::visit(*node
.value
->type
, ctx
);
138 LLVMAsmGenerator::visit(*node
.value
, ctx
);
140 LLVMAsmGenerator::visit(*node
.value
->type
, ctx
);
141 ctx
.out_
<< "* %" << node
.name
<< std::endl
;
144 static void visitAssignExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
146 const ast::AssignExp node
= reinterpret_cast<const ast::AssignExp
&> (node_
);
148 LLVMAsmGenerator::visit(*node
.dest
, ctx
);
150 if (node
.value
->nodeType
== ast::NumberExp::nodeTypeId() ||
151 node
.value
->nodeType
== ast::IdExp::nodeTypeId())
154 LLVMAsmGenerator::visit(*node
.value
->type
, ctx
);
156 LLVMAsmGenerator::visit(*node
.value
, ctx
);
159 LLVMAsmGenerator::visit(*node
.value
, ctx
);
160 ctx
.out_
<< std::endl
;
163 #define GEN_BINARY_EXP_EXT(Name, Type, Instr) \
164 static void Name(const ast::Node & node_, LLVMAsmGenerator & ctx) \
166 const ast::Type & node = reinterpret_cast<const ast::Type &> (node_); \
167 ctx.out_ << Instr" "; \
168 LLVMAsmGenerator::visit(*unreferencedType(node.type), ctx); \
169 LLVMAsmGenerator::visit(*node.left, ctx); \
171 LLVMAsmGenerator::visit(*node.right, ctx); \
174 #define GEN_BINARY_EXP(Type, Instr) GEN_BINARY_EXP_EXT(visit##Type, Type, Instr)
176 GEN_BINARY_EXP(AddExp
, "add")
177 GEN_BINARY_EXP(SubExp
, "sub")
178 GEN_BINARY_EXP(MulExp
, "mul")
179 GEN_BINARY_EXP_EXT(visitUDivExp
, DivExp
, "udiv")
180 GEN_BINARY_EXP_EXT(visitSDivExp
, DivExp
, "sdiv")
181 GEN_BINARY_EXP_EXT(visitFDivExp
, DivExp
, "fdiv")
182 GEN_BINARY_EXP_EXT(visitUModExp
, ModExp
, "urem")
183 GEN_BINARY_EXP_EXT(visitSModExp
, ModExp
, "srem")
184 GEN_BINARY_EXP_EXT(visitFModExp
, ModExp
, "frem")
186 GEN_BINARY_EXP(AndExp
, "and")
187 GEN_BINARY_EXP(OrExp
, "or")
188 GEN_BINARY_EXP(XorExp
, "xor")
190 GEN_BINARY_EXP(ShlExp
, "shl")
191 GEN_BINARY_EXP(AShrExp
, "ashr")
192 GEN_BINARY_EXP(LShrExp
, "lshr")
194 /// @todo check this is correct.
195 #define GEN_CMP_EXP(Type_, Instr) \
196 static void visit##Type_(const ast::Node & node_, LLVMAsmGenerator & ctx) \
198 const ast::Type_ & node = reinterpret_cast<const ast::Type_ &> (node_); \
199 ast::Type * type = unreferencedType(node.left->type); \
201 if (type->nodeType == ast::FloatType::nodeTypeId() || \
202 type->nodeType == ast::DoubleType::nodeTypeId()) \
203 ctx.out_ << "fcmp o"; \
206 ctx.out_ << "icmp "; \
207 if (node.nodeType == ast::LtExp::nodeTypeId() || \
208 node.nodeType == ast::LtEqExp::nodeTypeId() || \
209 node.nodeType == ast::GtExp::nodeTypeId() || \
210 node.nodeType == ast::GtEqExp::nodeTypeId()) \
212 if (type->nodeType == ast::IntegerType::nodeTypeId()) \
214 ast::IntegerType * itype = reinterpret_cast<ast::IntegerType *>(type); \
215 ctx.out_ << (itype->isSigned ? "s" : "u"); \
217 else if (type->nodeType == ast::BoolType::nodeTypeId()) \
221 ctx.out_ << Instr" "; \
222 LLVMAsmGenerator::visit(*type, ctx); \
223 LLVMAsmGenerator::visit(*node.left, ctx); \
225 LLVMAsmGenerator::visit(*node.right, ctx); \
228 GEN_CMP_EXP(EqExp
, "eq")
229 GEN_CMP_EXP(NeqExp
, "ne")
230 GEN_CMP_EXP(LtExp
, "lt")
231 GEN_CMP_EXP(LtEqExp
, "le")
232 GEN_CMP_EXP(GtExp
, "gt")
233 GEN_CMP_EXP(GtEqExp
, "ge")
235 #define GEN_BINARY_BOOL_EXP(Type, Op) \
236 static void visit##Type(const ast::Node & node_, LLVMAsmGenerator & ctx) \
238 const ast::Type node = reinterpret_cast<const ast::Type &> (node_); \
239 ctx.out_ << Op " i1 "; \
240 LLVMAsmGenerator::visit(*node.left, ctx); \
242 LLVMAsmGenerator::visit(*node.right, ctx); \
245 GEN_BINARY_BOOL_EXP(AndAndExp
, "and ")
246 GEN_BINARY_BOOL_EXP(OrOrExp
, "or ")
248 #define GEN_DIV_MOD_EXP(Name) \
249 static void visit##Name##Exp(const ast::Node & node_, LLVMAsmGenerator & ctx) \
251 const ast::Name##Exp node = reinterpret_cast<const ast::Name##Exp &> (node_); \
252 if (node.type->nodeType == ast::BoolType::nodeTypeId()) \
253 visitU##Name##Exp(node, ctx); \
254 else if (node.type->nodeType == ast::FloatType::nodeTypeId() || \
255 node.type->nodeType == ast::DoubleType::nodeTypeId()) \
256 visitF##Name##Exp(node, ctx); \
257 else if (node.type->nodeType == ast::IntegerType::nodeTypeId()) \
259 ast::IntegerType * type = reinterpret_cast<ast::IntegerType *>(node.type); \
261 visitS##Name##Exp(node, ctx) : visitU##Name##Exp(node, ctx); \
271 static void visitNegExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
273 const ast::NegExp node
= reinterpret_cast<const ast::NegExp
&> (node_
);
275 LLVMAsmGenerator::visit(*node
.type
, ctx
);
276 if (AST_IS_FLOATING_POINT_TYPE(node
.type
))
277 ctx
.out_
<< " 0.0, ";
280 LLVMAsmGenerator::visit(*node
.exp
, ctx
);
283 static void visitBangExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
285 const ast::BangExp node
= reinterpret_cast<const ast::BangExp
&> (node_
);
286 ctx
.out_
<< "xor i1 1, ";
287 LLVMAsmGenerator::visit(*node
.exp
, ctx
);
290 static void visitNotExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
292 const ast::NotExp node
= reinterpret_cast<const ast::NotExp
&> (node_
);
294 LLVMAsmGenerator::visit(*node
.type
, ctx
);
296 LLVMAsmGenerator::visit(*node
.exp
, ctx
);
299 static void visitExp(const ast::Node
& /*node_*/, LLVMAsmGenerator
& /*ctx*/)
301 assert_msg(false, "unimplemented method");
304 static void visitVoidExp(const ast::Node
& /*node_*/, LLVMAsmGenerator
& /*ctx*/)
308 static void visitIdExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
310 const ast::IdExp node
= reinterpret_cast<const ast::IdExp
&> (node_
);
311 ctx
.out_
<< "%" << node
.symbol
->name
;
314 static void visitAtExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
316 const ast::AtExp node
= reinterpret_cast<const ast::AtExp
&> (node_
);
317 LLVMAsmGenerator::visit(*node
.exp
, ctx
);
320 static void visitCallExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
322 const ast::CallExp node
= reinterpret_cast<const ast::CallExp
&> (node_
);
323 ctx
.out_
<< " call ";
324 LLVMAsmGenerator::visit(*node
.type
, ctx
);
325 ctx
.out_
<< " @" << node
.id
<< "(";
327 for (ast::CallExp::args_t::iterator it
= node
.args
->begin();
328 it
!= node
.args
->end(); it
++)
330 if (it
!= node
.args
->begin())
332 LLVMAsmGenerator::visit(*unreferencedType((*it
)->type
), ctx
);
333 LLVMAsmGenerator::visit(*(*it
), ctx
);
337 if (node
.type
->nodeType
== ast::VoidType::nodeTypeId())
338 ctx
.out_
<< std::endl
;
341 static void visitNumberExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
343 const ast::NumberExp node
= reinterpret_cast<const ast::NumberExp
&> (node_
);
344 if (node
.type
->nodeType
== ast::FloatType::nodeTypeId())
345 /// @todo find the right way to print floating point numbers
346 ctx
.out_
<< std::setprecision(1000) << (float)node
.number
;
348 ctx
.out_
<< std::setprecision(1000) << node
.number
;
351 static void visitLabel(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
353 const ast::Label node
= reinterpret_cast<const ast::Label
&> (node_
);
354 ctx
.out_
<< " br label %" << node
.name
<< " ; dummy branch to start a block" << std::endl
355 << node
.name
<< ":" << std::endl
;
358 static void visitGoto(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
360 const ast::Goto node
= reinterpret_cast<const ast::Goto
&> (node_
);
361 ctx
.out_
<< " br label %" << node
.label
<< std::endl
;
364 static void visitReturn(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
366 const ast::Return node
= reinterpret_cast<const ast::Return
&> (node_
);
370 LLVMAsmGenerator::visit(*node
.exp
->type
, ctx
);
371 LLVMAsmGenerator::visit(*node
.exp
, ctx
);
372 ctx
.out_
<< std::endl
;
376 #define VISIT_COND_BRANCH(Type) \
378 LLVMAsmGenerator::visit(const Type & node) \
380 visit((ConditionalBranch)node); \
383 VISIT_COND_BRANCH(If
)
384 VISIT_COND_BRANCH(While
)
385 VISIT_COND_BRANCH(DoWhile
)
388 static void visitConditionalBranch(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
390 const ast::ConditionalBranch node
= reinterpret_cast<const ast::ConditionalBranch
&> (node_
);
391 ctx
.out_
<< " br i1 ";
392 LLVMAsmGenerator::visit(*node
.cond
, ctx
);
393 ctx
.out_
<< ", label %" << node
.trueLabel
->name
<< ", label %"
394 << node
.falseLabel
->name
<< std::endl
;
397 static void visitType(const ast::Node
& /*node_*/, LLVMAsmGenerator
& /*ctx*/)
399 //assert_msg(false, "we should never reach this code");
402 static void visitNumberType(const ast::Node
& /*node_*/, LLVMAsmGenerator
& /*ctx*/)
404 assert_msg(false, "we should never reach this code");
407 static void visitBoolType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
409 const ast::BoolType node
= reinterpret_cast<const ast::BoolType
&> (node_
);
413 static void visitVoidType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
415 const ast::VoidType node
= reinterpret_cast<const ast::VoidType
&> (node_
);
419 static void visitIntegerType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
421 const ast::IntegerType node
= reinterpret_cast<const ast::IntegerType
&> (node_
);
422 ctx
.out_
<< "i" << node
.size
<< " ";
425 static void visitFloatType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
427 const ast::FloatType node
= reinterpret_cast<const ast::FloatType
&> (node_
);
428 ctx
.out_
<< "float ";
431 static void visitDoubleType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
433 const ast::DoubleType node
= reinterpret_cast<const ast::DoubleType
&> (node_
);
434 ctx
.out_
<< "double ";
437 static void visitPointerType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
439 const ast::PointerType node
= reinterpret_cast<const ast::PointerType
&> (node_
);
440 LLVMAsmGenerator::visit(*node
.type
, ctx
);
444 static void visitReferenceType(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
446 const ast::ReferenceType node
= reinterpret_cast<const ast::ReferenceType
&> (node_
);
447 LLVMAsmGenerator::visit(*node
.type
, ctx
);
451 static void visitCastExp(const ast::Node
& node_
, LLVMAsmGenerator
& ctx
)
453 const ast::CastExp node
= reinterpret_cast<const ast::CastExp
&> (node_
);
454 ast::Type
* from
= unreferencedType(node
.exp
->type
);
455 ast::Type
* to
= unreferencedType(node
.type
);
459 LLVMAsmGenerator::castTableKey_t key
=
460 std::make_pair(from
->nodeType
, to
->nodeType
);
461 assert(ctx
.castTable_
.count(key
) > 0);
462 (ctx
.*ctx
.castTable_
[key
])(node
);
466 LLVMAsmGenerator::castBoolToBool(const ast::CastExp
& node
)
468 out_
<< "add i1 0, ";
469 LLVMAsmGenerator::visit(*node
.exp
, *this);
473 #define SIMPLE_CAST(Type1, Type2, CastInstr) \
475 LLVMAsmGenerator::cast##Type1##To##Type2(const ast::CastExp & node) \
477 out_ << CastInstr" "; \
478 LLVMAsmGenerator::visit(*node.exp->type, *this); \
480 LLVMAsmGenerator::visit(*node.exp, *this); \
482 LLVMAsmGenerator::visit(*node.type, *this); \
486 SIMPLE_CAST(Bool
, Float
, "uitofp")
487 SIMPLE_CAST(Bool
, Double
, "uitofp")
488 SIMPLE_CAST(Float
, Double
, "fpext")
489 SIMPLE_CAST(Integer
, Pointer
, "inttoptr")
490 SIMPLE_CAST(Pointer
, Integer
, "ptrtoint")
493 LLVMAsmGenerator::castBoolToInteger(const ast::CastExp
& node
)
495 ast::IntegerType
* to
= reinterpret_cast<ast::IntegerType
*>(node
.type
);
499 out_
<< "add i1 0, ";
500 LLVMAsmGenerator::visit(*node
.exp
, *this);
505 LLVMAsmGenerator::visit(*node
.exp
, *this);
507 LLVMAsmGenerator::visit(*to
, *this);
512 LLVMAsmGenerator::castIntegerToInteger(const ast::CastExp
& node
)
514 ast::IntegerType
* from
= ast::ast_cast
<ast::IntegerType
*>(node
.exp
->type
);
515 ast::IntegerType
* to
= ast::ast_cast
<ast::IntegerType
*>(node
.type
);
517 if (from
->size
== to
->size
)
519 out_
<< "add i" << to
->size
<< " 0, ";
520 LLVMAsmGenerator::visit(*node
.exp
, *this);
524 if (from
->size
> to
->size
)
526 else if (from
->isSigned
)
530 out_
<< " i" << from
->size
<< " ";
531 LLVMAsmGenerator::visit(*node
.exp
, *this);
532 out_
<< " to i" << to
->size
;
535 #define INTEGER_TO_FLOAT(Float, Str) \
537 LLVMAsmGenerator::castIntegerTo##Float(const ast::CastExp & node) \
539 ast::IntegerType * from = \
540 ast::ast_cast<ast::IntegerType *>(node.exp->type); \
542 out_ << (from->isSigned ? "sitofp " : "uitofp "); \
543 LLVMAsmGenerator::visit(*from, *this); \
544 LLVMAsmGenerator::visit(*node.exp, *this); \
545 out_ << " to " Str; \
548 INTEGER_TO_FLOAT(Float
, "float")
549 INTEGER_TO_FLOAT(Double
, "double")
552 LLVMAsmGenerator::castIntegerToBool(const ast::CastExp
& node
)
555 LLVMAsmGenerator::visit(*node
.exp
->type
, *this);
557 LLVMAsmGenerator::visit(*node
.exp
, *this);
560 #define FLOAT_TO_BOOL(Type, Str) \
562 LLVMAsmGenerator::cast##Type##ToBool(const ast::CastExp & /*node*/) \
564 /** @todo throw a warning */ \
565 assert_msg(false, "casting float to bool is a non sense."); \
568 FLOAT_TO_BOOL(Float
, "float")
569 FLOAT_TO_BOOL(Double
, "double")
572 LLVMAsmGenerator::initBase()
574 #define REGISTER_METHOD(Class) \
575 registerMethod(ast::Class::nodeTypeId(), visit##Class)
577 REGISTER_METHOD(FunctionDecl
);
578 REGISTER_METHOD(Function
);
580 REGISTER_METHOD(VarDecl
);
582 REGISTER_METHOD(LoadVar
);
583 REGISTER_METHOD(StoreVar
);
585 REGISTER_METHOD(Exp
);
586 REGISTER_METHOD(VoidExp
);
587 REGISTER_METHOD(IdExp
);
588 REGISTER_METHOD(AtExp
);
589 REGISTER_METHOD(NumberExp
);
590 REGISTER_METHOD(CallExp
);
592 REGISTER_METHOD(AssignExp
);
593 REGISTER_METHOD(AddExp
);
594 REGISTER_METHOD(SubExp
);
595 REGISTER_METHOD(MulExp
);
596 REGISTER_METHOD(DivExp
);
597 REGISTER_METHOD(ModExp
);
599 REGISTER_METHOD(AndExp
);
600 REGISTER_METHOD(OrExp
);
601 REGISTER_METHOD(XorExp
);
603 REGISTER_METHOD(ShlExp
);
604 REGISTER_METHOD(AShrExp
);
605 REGISTER_METHOD(LShrExp
);
607 REGISTER_METHOD(AndAndExp
);
608 REGISTER_METHOD(OrOrExp
);
610 REGISTER_METHOD(NotExp
);
611 REGISTER_METHOD(BangExp
);
612 REGISTER_METHOD(NegExp
);
614 REGISTER_METHOD(EqExp
);
615 REGISTER_METHOD(NeqExp
);
616 REGISTER_METHOD(LtExp
);
617 REGISTER_METHOD(LtEqExp
);
618 REGISTER_METHOD(GtExp
);
619 REGISTER_METHOD(GtEqExp
);
621 REGISTER_METHOD(Label
);
622 REGISTER_METHOD(Goto
);
623 REGISTER_METHOD(Return
);
624 REGISTER_METHOD(ConditionalBranch
);
626 REGISTER_METHOD(Type
);
627 REGISTER_METHOD(VoidType
);
628 REGISTER_METHOD(BoolType
);
629 REGISTER_METHOD(NumberType
);
630 REGISTER_METHOD(IntegerType
);
631 REGISTER_METHOD(FloatType
);
632 REGISTER_METHOD(DoubleType
);
633 REGISTER_METHOD(PointerType
);
634 REGISTER_METHOD(ReferenceType
);
636 REGISTER_METHOD(CastExp
);
638 completeWith
<ConstBrowser
<LLVMAsmGenerator
> >();