zeek/src/script_opt/ZAM/Expr.cc

1327 lines
39 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
// Methods for traversing Expr AST nodes to generate ZAM code.
#include "zeek/Desc.h"
#include "zeek/Reporter.h"
#include "zeek/script_opt/ZAM/Compile.h"
namespace zeek::detail {
const ZAMStmt ZAMCompiler::CompileExpr(const Expr* e) {
switch ( e->Tag() ) {
case EXPR_INCR:
case EXPR_DECR: return CompileIncrExpr(static_cast<const IncrExpr*>(e));
case EXPR_APPEND_TO: return CompileAppendToExpr(static_cast<const AppendToExpr*>(e));
case EXPR_ADD_TO: return CompileAddToExpr(static_cast<const AddToExpr*>(e));
case EXPR_REMOVE_FROM: return CompileRemoveFromExpr(static_cast<const RemoveFromExpr*>(e));
case EXPR_ASSIGN: return CompileAssignExpr(static_cast<const AssignExpr*>(e));
case EXPR_INDEX_ASSIGN: {
auto iae = static_cast<const IndexAssignExpr*>(e);
auto t = iae->GetOp1()->GetType()->Tag();
if ( t == TYPE_VECTOR )
return AssignVecElems(iae);
ASSERT(t == TYPE_TABLE);
return AssignTableElem(iae);
}
case EXPR_FIELD_LHS_ASSIGN: {
auto flhs = static_cast<const FieldLHSAssignExpr*>(e);
return CompileFieldLHSAssignExpr(flhs);
}
case EXPR_SCHEDULE: return CompileScheduleExpr(static_cast<const ScheduleExpr*>(e));
case EXPR_EVENT: {
auto ee = static_cast<const EventExpr*>(e);
auto h = ee->Handler().Ptr();
auto args = ee->Args();
return EventHL(h, args);
}
default: reporter->InternalError("bad statement type in ZAMCompile::CompileExpr");
}
}
const ZAMStmt ZAMCompiler::CompileIncrExpr(const IncrExpr* e) {
auto target = e->Op()->AsRefExpr()->GetOp1()->AsNameExpr();
if ( target->GetType()->Tag() == TYPE_INT ) {
if ( e->Tag() == EXPR_INCR )
return IncrIV(target);
else
return DecrIV(target);
}
if ( e->Tag() == EXPR_INCR )
return IncrUV(target);
else
return DecrUV(target);
}
const ZAMStmt ZAMCompiler::CompileAppendToExpr(const AppendToExpr* e) {
auto n1 = e->GetOp1()->AsNameExpr();
auto op2 = e->GetOp2();
auto n2 = op2->Tag() == EXPR_NAME ? op2->AsNameExpr() : nullptr;
auto cc = op2->Tag() != EXPR_NAME ? op2->AsConstExpr() : nullptr;
if ( n1->GetType()->Yield()->Tag() == TYPE_ANY )
return n2 ? AppendToAnyVecVV(n1, n2) : AppendToAnyVecVC(n1, cc);
return n2 ? AppendToVV(n1, n2) : AppendToVC(n1, cc);
}
const ZAMStmt ZAMCompiler::CompileAddToExpr(const AddToExpr* e) {
auto op1 = e->GetOp1();
auto t1 = op1->GetType()->Tag();
auto op2 = e->GetOp2();
auto n2 = op2->Tag() == EXPR_NAME ? op2->AsNameExpr() : nullptr;
auto cc = op2->Tag() != EXPR_NAME ? op2->AsConstExpr() : nullptr;
if ( op1->Tag() == EXPR_FIELD ) {
assert(t1 == TYPE_PATTERN);
auto f = op1->AsFieldExpr()->Field();
auto n1 = op1->GetOp1()->AsNameExpr();
ZInstI z;
if ( n2 ) {
z = ZInstI(OP_ADDPATTERNTOFIELD_VVi, FrameSlot(n1), FrameSlot(n2), f);
z.op_type = OP_VVV_I3;
}
else {
z = ZInstI(OP_ADDPATTERNTOFIELD_VCi, FrameSlot(n1), f, cc);
z.op_type = OP_VVC_I2;
}
z.SetType(n2 ? n2->GetType() : cc->GetType());
return AddInst(z);
}
auto n1 = op1->AsNameExpr();
if ( t1 == TYPE_PATTERN )
return n2 ? ExtendPatternVV(n1, n2) : ExtendPatternVC(n1, cc);
if ( t1 == TYPE_VECTOR )
return n2 ? AddVecToVecVV(n1, n2) : AddVecToVecVC(n1, cc);
assert(t1 == TYPE_TABLE);
return n2 ? AddTableToTableVV(n1, n2) : AddTableToTableVC(n1, cc);
}
const ZAMStmt ZAMCompiler::CompileRemoveFromExpr(const RemoveFromExpr* e) {
auto n1 = e->GetOp1()->AsNameExpr();
auto op2 = e->GetOp2();
auto n2 = op2->Tag() == EXPR_NAME ? op2->AsNameExpr() : nullptr;
auto cc = op2->Tag() != EXPR_NAME ? op2->AsConstExpr() : nullptr;
return n2 ? RemoveTableFromTableVV(n1, n2) : RemoveTableFromTableVC(n1, cc);
}
const ZAMStmt ZAMCompiler::CompileAssignExpr(const AssignExpr* e) {
auto op1 = e->GetOp1();
auto op2 = e->GetOp2();
auto lhs = op1->AsRefExpr()->GetOp1()->AsNameExpr();
auto lt = lhs->GetType().get();
auto rhs = op2.get();
auto r1 = rhs->GetOp1();
if ( rhs->Tag() == EXPR_INDEX && (r1->Tag() == EXPR_NAME || r1->Tag() == EXPR_CONST) )
return CompileAssignToIndex(lhs, rhs->AsIndexExpr());
switch ( rhs->Tag() ) {
#include "ZAM-DirectDefs.h"
default: break;
}
auto rt = rhs->GetType();
auto r2 = rhs->GetOp2();
auto r3 = rhs->GetOp3();
if ( rhs->Tag() == EXPR_NAME )
return AssignVV(lhs, rhs->AsNameExpr());
if ( rhs->Tag() == EXPR_CONST )
return AssignVC(lhs, rhs->AsConstExpr());
if ( rhs->Tag() == EXPR_IN && r1->Tag() == EXPR_LIST ) {
// r2 can be a constant due to propagating "const"
// globals, for example.
if ( r2->Tag() == EXPR_NAME ) {
auto r2n = r2->AsNameExpr();
if ( r2->GetType()->Tag() == TYPE_TABLE )
return L_In_TVLV(lhs, r1->AsListExpr(), r2n);
return L_In_VecVLV(lhs, r1->AsListExpr(), r2n);
}
auto r2c = r2->AsConstExpr();
if ( r2->GetType()->Tag() == TYPE_TABLE )
return L_In_TVLC(lhs, r1->AsListExpr(), r2c);
return L_In_VecVLC(lhs, r1->AsListExpr(), r2c);
}
if ( rhs->Tag() == EXPR_ANY_INDEX )
return AnyIndexVVi(lhs, r1->AsNameExpr(), rhs->AsAnyIndexExpr()->Index());
if ( rhs->Tag() == EXPR_LAMBDA )
return BuildLambda(lhs, rhs->AsLambdaExpr());
if ( rhs->Tag() == EXPR_COND && r1->GetType()->Tag() == TYPE_VECTOR )
return Bool_Vec_CondVVVV(lhs, r1->AsNameExpr(), r2->AsNameExpr(), r3->AsNameExpr());
if ( rhs->Tag() == EXPR_COND && r2->IsConst() && r3->IsConst() ) {
// Split into two statement, given we don't support
// two constants in a single statement.
auto n1 = r1->AsNameExpr();
auto c2 = r2->AsConstExpr();
auto c3 = r3->AsConstExpr();
(void)CondC1VVC(lhs, n1, c2);
return CondC2VVC(lhs, n1, c3);
}
if ( r1 && r2 && ! r3 ) {
auto v1 = IsVector(r1->GetType()->Tag());
auto v2 = IsVector(r2->GetType()->Tag());
if ( v1 != v2 && rhs->Tag() != EXPR_IN ) {
reporter->Error("deprecated mixed vector/scalar operation not supported for ZAM compiling");
return ErrorStmt();
}
}
if ( r1 && r1->IsConst() )
#include "ZAM-GenExprsDefsC1.h"
else if ( r2 && r2->IsConst() )
#include "ZAM-GenExprsDefsC2.h"
else if ( r3 && r3->IsConst() )
#include "ZAM-GenExprsDefsC3.h"
else
#include "ZAM-GenExprsDefsV.h"
}
const ZAMStmt ZAMCompiler::CompileAssignToIndex(const NameExpr* lhs, const IndexExpr* rhs) {
auto aggr = rhs->GetOp1();
auto const_aggr = aggr->Tag() == EXPR_CONST;
auto indexes_expr = rhs->GetOp2()->AsListExpr();
auto indexes = indexes_expr->Exprs();
auto n = const_aggr ? nullptr : aggr->AsNameExpr();
auto con = const_aggr ? aggr->AsConstExpr() : nullptr;
if ( indexes.length() == 1 && indexes[0]->GetType()->Tag() == TYPE_VECTOR &&
aggr->GetType()->Tag() != TYPE_TABLE ) {
auto index1 = indexes[0];
if ( index1->Tag() == EXPR_CONST ) {
reporter->Error("constant vector indexes not supported for ZAM compiling");
return ErrorStmt();
}
auto index = index1->AsNameExpr();
auto ind_t = index->GetType()->AsVectorType();
if ( IsBool(ind_t->Yield()->Tag()) )
return const_aggr ? IndexVecBoolSelectVCV(lhs, con, index) : IndexVecBoolSelectVVV(lhs, n, index);
return const_aggr ? IndexVecIntSelectVCV(lhs, con, index) : IndexVecIntSelectVVV(lhs, n, index);
}
if ( rhs->IsInsideWhen() ) {
if ( const_aggr )
return WhenIndexVCL(lhs, con, indexes_expr);
else
return WhenIndexVVL(lhs, n, indexes_expr);
}
if ( const_aggr )
return IndexVCL(lhs, con, indexes_expr);
else
return IndexVVL(lhs, n, indexes_expr);
}
const ZAMStmt ZAMCompiler::CompileFieldLHSAssignExpr(const FieldLHSAssignExpr* e) {
auto lhs = e->Op1()->AsNameExpr();
auto rhs = e->Op2();
auto field = e->Field();
if ( rhs->Tag() == EXPR_NAME )
return Field_LHS_AssignFV(e, rhs->AsNameExpr());
if ( rhs->Tag() == EXPR_CONST )
return Field_LHS_AssignFC(e, rhs->AsConstExpr());
auto r1 = rhs->GetOp1();
auto r2 = rhs->GetOp2();
if ( rhs->Tag() == EXPR_FIELD ) {
auto rhs_f = rhs->AsFieldExpr();
if ( r1->Tag() == EXPR_NAME )
return Field_LHS_AssignFVi(e, r1->AsNameExpr(), rhs_f->Field());
return Field_LHS_AssignFCi(e, r1->AsConstExpr(), rhs_f->Field());
}
if ( r1 && r1->IsConst() )
#include "ZAM-GenFieldsDefsC1.h"
else if ( r2 && r2->IsConst() )
#include "ZAM-GenFieldsDefsC2.h"
else
#include "ZAM-GenFieldsDefsV.h"
}
const ZAMStmt ZAMCompiler::CompileScheduleExpr(const ScheduleExpr* e) {
auto event = e->Event();
auto when = e->When();
auto event_args = event->Args();
auto handler = event->Handler();
bool is_interval = when->GetType()->Tag() == TYPE_INTERVAL;
if ( when->Tag() == EXPR_NAME )
return ScheduleViHL(when->AsNameExpr(), is_interval, handler.Ptr(), event_args);
else
return ScheduleCiHL(when->AsConstExpr(), is_interval, handler.Ptr(), event_args);
}
const ZAMStmt ZAMCompiler::CompileSchedule(const NameExpr* n, const ConstExpr* c, int is_interval, EventHandler* h,
const ListExpr* l) {
int len = l->Exprs().length();
ZInstI z;
if ( len == 0 ) {
z = n ? ZInstI(OP_SCHEDULE0_ViH, FrameSlot(n), is_interval) : ZInstI(OP_SCHEDULE0_CiH, is_interval, c);
z.op_type = n ? OP_VV_I2 : OP_VC_I1;
}
else {
if ( n ) {
z = ZInstI(OP_SCHEDULE_ViHL, FrameSlot(n), is_interval);
z.op_type = OP_VV_I2;
}
else {
z = ZInstI(OP_SCHEDULE_CiHL, is_interval, c);
z.op_type = OP_VC_I1;
}
z.aux = InternalBuildVals(l);
}
z.event_handler = h;
return AddInst(z);
}
const ZAMStmt ZAMCompiler::CompileEvent(EventHandler* h, const ListExpr* l) {
auto exprs = l->Exprs();
unsigned int n = exprs.length();
bool all_vars = true;
for ( auto i = 0U; i < n; ++i )
if ( exprs[i]->Tag() == EXPR_CONST ) {
all_vars = false;
break;
}
if ( n > 4 || ! all_vars ) { // do generic form
ZInstI z(OP_EVENT_HL);
z.aux = InternalBuildVals(l);
z.event_handler = h;
return AddInst(z);
}
ZInstI z;
z.event_handler = h;
if ( n == 0 ) {
z.op = OP_EVENT0_X;
z.op_type = OP_X;
}
else {
auto n0 = exprs[0]->AsNameExpr();
z.v1 = FrameSlot(n0);
z.t = n0->GetType();
if ( n == 1 ) {
z.op = OP_EVENT1_V;
z.op_type = OP_V;
}
else {
auto n1 = exprs[1]->AsNameExpr();
z.v2 = FrameSlot(n1);
z.t2 = n1->GetType();
if ( n == 2 ) {
z.op = OP_EVENT2_VV;
z.op_type = OP_VV;
}
else {
z.aux = InternalBuildVals(l);
auto n2 = exprs[2]->AsNameExpr();
z.v3 = FrameSlot(n2);
if ( n == 3 ) {
z.op = OP_EVENT3_VVV;
z.op_type = OP_VVV;
}
else {
z.op = OP_EVENT4_VVVV;
z.op_type = OP_VVVV;
auto n3 = exprs[3]->AsNameExpr();
z.v4 = FrameSlot(n3);
}
}
}
}
return AddInst(z);
}
const ZAMStmt ZAMCompiler::CompileInExpr(const NameExpr* n1, const NameExpr* n2, const ConstExpr* c2,
const NameExpr* n3, const ConstExpr* c3) {
const Expr* op2 = n2;
const Expr* op3 = n3;
if ( ! op2 )
op2 = c2;
if ( ! op3 )
op3 = c3;
ZOp a;
auto& op2_t = op2->GetType();
auto& op3_t = op3->GetType();
if ( op3_t->Tag() == TYPE_TABLE ) {
if ( op3_t->AsTableType()->IsPatternIndex() && op2_t->Tag() == TYPE_STRING )
a = n2 ? OP_STR_IN_PAT_TBL_VVV : OP_STR_IN_PAT_TBL_VCV;
else
a = n2 ? OP_VAL_IS_IN_TABLE_VVV : OP_CONST_IS_IN_TABLE_VCV;
}
else if ( op2->GetType()->Tag() == TYPE_PATTERN )
a = n2 ? (n3 ? OP_P_IN_S_VVV : OP_P_IN_S_VVC) : OP_P_IN_S_VCV;
else if ( op2->GetType()->Tag() == TYPE_STRING )
a = n2 ? (n3 ? OP_S_IN_S_VVV : OP_S_IN_S_VVC) : OP_S_IN_S_VCV;
else if ( op2->GetType()->Tag() == TYPE_ADDR && op3->GetType()->Tag() == TYPE_SUBNET )
a = n2 ? (n3 ? OP_A_IN_S_VVV : OP_A_IN_S_VVC) : OP_A_IN_S_VCV;
else
reporter->InternalError("bad types when compiling \"in\"");
auto s2 = n2 ? FrameSlot(n2) : 0;
auto s3 = n3 ? FrameSlot(n3) : 0;
auto s1 = Frame1Slot(n1, a);
ZInstI z;
if ( n2 ) {
if ( n3 )
z = ZInstI(a, s1, s2, s3);
else
z = ZInstI(a, s1, s2, c3);
}
else
z = ZInstI(a, s1, s3, c2);
TypePtr zt;
if ( c2 )
zt = c2->GetType();
else if ( c3 )
zt = c3->GetType();
else
zt = n2->GetType();
z.SetType(zt);
return AddInst(z);
}
const ZAMStmt ZAMCompiler::CompileInExpr(const NameExpr* n1, const ListExpr* l, const NameExpr* n2,
const ConstExpr* c) {
auto& l_e = l->Exprs();
int n = l_e.length();
// Look for a very common special case: l is a single-element list,
// and n2 is present rather than c.
if ( n == 1 && n2 ) {
ZInstI z;
bool is_vec = n2->GetType()->Tag() == TYPE_VECTOR;
if ( l_e[0]->Tag() == EXPR_NAME ) {
auto l_e0_n = l_e[0]->AsNameExpr();
ZOp op = is_vec ? OP_VAL_IS_IN_VECTOR_VVV : OP_VAL_IS_IN_TABLE_VVV;
z = GenInst(op, n1, l_e0_n, n2);
}
else {
auto l_e0_c = l_e[0]->AsConstExpr();
ZOp op = is_vec ? OP_CONST_IS_IN_VECTOR_VCV : OP_CONST_IS_IN_TABLE_VCV;
z = GenInst(op, n1, l_e0_c, n2);
}
z.t = l_e[0]->GetType();
return AddInst(z);
}
// Also somewhat common is a 2-element index. Here, one or both of
// the elements might be a constant, which makes things messier.
if ( n == 2 && n2 && (l_e[0]->Tag() == EXPR_NAME || l_e[1]->Tag() == EXPR_NAME) ) {
auto is_name0 = l_e[0]->Tag() == EXPR_NAME;
auto is_name1 = l_e[1]->Tag() == EXPR_NAME;
auto l_e0_n = is_name0 ? l_e[0]->AsNameExpr() : nullptr;
auto l_e1_n = is_name1 ? l_e[1]->AsNameExpr() : nullptr;
auto l_e0_c = is_name0 ? nullptr : l_e[0]->AsConstExpr();
auto l_e1_c = is_name1 ? nullptr : l_e[1]->AsConstExpr();
ZInstI z;
if ( l_e0_n && l_e1_n ) {
z = GenInst(OP_VAL2_IS_IN_TABLE_VVVV, n1, l_e0_n, l_e1_n, n2);
z.t2 = l_e0_n->GetType();
}
else if ( l_e0_n ) {
ASSERT(l_e1_c);
z = GenInst(OP_VAL2_IS_IN_TABLE_VVVC, n1, l_e0_n, n2, l_e1_c);
z.t2 = l_e0_n->GetType();
}
else if ( l_e1_n ) {
ASSERT(l_e0_c);
z = GenInst(OP_VAL2_IS_IN_TABLE_VVCV, n1, l_e1_n, n2, l_e0_c);
z.t2 = l_e1_n->GetType();
}
else {
// Ugh, both are constants. Assign first to
// a temporary.
ASSERT(l_e0_c);
ASSERT(l_e1_c);
auto slot = TempForConst(l_e0_c);
z = ZInstI(OP_VAL2_IS_IN_TABLE_VVVC, FrameSlot(n1), slot, FrameSlot(n2), l_e1_c);
z.op_type = OP_VVVC;
z.t2 = l_e0_c->GetType();
}
return AddInst(z);
}
auto aggr = n2 ? (Expr*)n2 : (Expr*)c;
ASSERT(aggr->GetType()->Tag() != TYPE_VECTOR);
ZOp op = n2 ? OP_LIST_IS_IN_TABLE_VV : OP_LIST_IS_IN_TABLE_VC;
ZInstI z;
if ( n2 )
z = ZInstI(op, Frame1Slot(n1, op), FrameSlot(n2));
else
z = ZInstI(op, Frame1Slot(n1, op), c);
z.aux = InternalBuildVals(l);
return AddInst(z);
}
const ZAMStmt ZAMCompiler::CompileIndex(const NameExpr* n1, const NameExpr* n2, const ListExpr* l, bool in_when) {
return CompileIndex(n1, FrameSlot(n2), n2->GetType(), l, in_when);
}
const ZAMStmt ZAMCompiler::CompileIndex(const NameExpr* n, const ConstExpr* c, const ListExpr* l, bool in_when) {
auto tmp = TempForConst(c);
return CompileIndex(n, tmp, c->GetType(), l, in_when);
}
const ZAMStmt ZAMCompiler::CompileIndex(const NameExpr* n1, int n2_slot, const TypePtr& n2t, const ListExpr* l,
bool in_when) {
ZInstI z;
int n = l->Exprs().length();
auto n2tag = n2t->Tag();
// Whether this is an instance of indexing a table[pattern] of X
// with a string.
bool is_pat_str_ind = false;
if ( n2tag == TYPE_TABLE && n == 1 ) {
auto& ind_types = n2t->AsTableType()->GetIndices();
auto& ind_type0 = ind_types->GetTypes()[0];
auto ind = l->Exprs()[0];
if ( ind_type0->Tag() == TYPE_PATTERN && ind->GetType()->Tag() == TYPE_STRING )
is_pat_str_ind = true;
}
if ( n == 1 && ! in_when ) {
auto ind = l->Exprs()[0];
auto var_ind = ind->Tag() == EXPR_NAME;
auto n3 = var_ind ? ind->AsNameExpr() : nullptr;
auto c3 = var_ind ? nullptr : ind->AsConstExpr();
zeek_uint_t c = 0;
if ( ! var_ind ) {
if ( ind->GetType()->Tag() == TYPE_COUNT )
c = c3->Value()->AsCount();
else if ( ind->GetType()->Tag() == TYPE_INT )
c = c3->Value()->AsInt();
}
if ( n2tag == TYPE_STRING ) {
if ( n3 ) {
int n3_slot = FrameSlot(n3);
auto zop = OP_INDEX_STRING_VVV;
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, n3_slot);
}
else {
auto zop = OP_INDEX_STRINGC_VVV;
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, c);
z.op_type = OP_VVV_I3;
}
return AddInst(z);
}
if ( n2tag == TYPE_VECTOR ) {
auto n2_yt = n2t->AsVectorType()->Yield();
bool is_any = n2_yt->Tag() == TYPE_ANY;
if ( n3 ) {
int n3_slot = FrameSlot(n3);
ZOp zop;
if ( in_when )
zop = OP_WHEN_INDEX_VEC_VVV;
else if ( is_any )
zop = OP_INDEX_ANY_VEC_VVV;
else
zop = OP_INDEX_VEC_VVV;
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, n3_slot);
}
else {
ZOp zop;
if ( in_when )
zop = OP_WHEN_INDEX_VECC_VVV;
else if ( is_any )
zop = OP_INDEX_ANY_VECC_VVV;
else
zop = OP_INDEX_VECC_VVV;
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, c);
z.op_type = OP_VVV_I3;
}
z.SetType(n1->GetType());
return AddInst(z);
}
if ( n2tag == TYPE_TABLE ) {
if ( is_pat_str_ind ) {
auto n1_slot = Frame1Slot(n1, OP1_WRITE);
if ( n3 ) {
int n3_slot = FrameSlot(n3);
z = ZInstI(OP_TABLE_PATSTR_INDEX_VVV, n1_slot, n2_slot, n3_slot);
}
else
z = ZInstI(OP_TABLE_PATSTR_INDEX_VVC, n1_slot, n2_slot, c3);
}
else if ( n3 ) {
int n3_slot = FrameSlot(n3);
auto zop = AssignmentFlavor(OP_TABLE_INDEX1_VVV, n1->GetType()->Tag());
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, n3_slot);
z.SetType(n3->GetType());
}
else {
ASSERT(c3);
auto zop = AssignmentFlavor(OP_TABLE_INDEX1_VVC, n1->GetType()->Tag());
z = ZInstI(zop, Frame1Slot(n1, zop), n2_slot, c3);
}
if ( pfs->HasSideEffects(SideEffectsOp::READ, n2t) ) {
z.aux = new ZInstAux(0);
z.aux->can_change_non_locals = true;
}
return AddInst(z);
}
}
auto indexes = l->Exprs();
ZOp op;
switch ( n2tag ) {
case TYPE_VECTOR:
op = in_when ? OP_WHEN_INDEX_VEC_SLICE_VV : OP_INDEX_VEC_SLICE_VV;
z = ZInstI(op, Frame1Slot(n1, op), n2_slot);
z.SetType(n2t);
break;
case TYPE_TABLE:
if ( in_when ) {
if ( is_pat_str_ind )
op = OP_WHEN_PATSTR_INDEX_VV;
else
op = OP_WHEN_TABLE_INDEX_VV;
}
else
op = OP_TABLE_INDEX_VV;
z = ZInstI(op, Frame1Slot(n1, op), n2_slot);
z.SetType(n1->GetType());
break;
case TYPE_STRING:
op = OP_INDEX_STRING_SLICE_VV;
z = ZInstI(op, Frame1Slot(n1, op), n2_slot);
z.SetType(n1->GetType());
break;
default: reporter->InternalError("bad aggregate type when compiling index");
}
z.aux = InternalBuildVals(l);
z.CheckIfManaged(n1->GetType());
return AddInst(z);
}
const ZAMStmt ZAMCompiler::BuildLambda(const NameExpr* n, LambdaExpr* le) {
return BuildLambda(Frame1Slot(n, OP1_WRITE), le);
}
const ZAMStmt ZAMCompiler::BuildLambda(int n_slot, LambdaExpr* le) {
auto& captures = le->GetCaptures();
int ncaptures = captures ? captures->size() : 0;
auto aux = new ZInstAux(ncaptures);
aux->primary_func = le->PrimaryFunc();
aux->lambda_name = le->Name();
aux->id_val = le->Ingredients()->GetID();
for ( int i = 0; i < ncaptures; ++i ) {
auto& id_i = (*captures)[i].Id();
if ( pf->WhenLocals().count(id_i.get()) > 0 )
aux->Add(i, nullptr);
else
aux->Add(i, FrameSlot(id_i), id_i->GetType());
}
auto z = ZInstI(OP_LAMBDA_VV, n_slot, le->PrimaryFunc()->FrameSize());
z.op_type = OP_VV_I2;
z.aux = aux;
return AddInst(z);
}
const ZAMStmt ZAMCompiler::AssignVecElems(const Expr* e) {
auto index_assign = e->AsIndexAssignExpr();
auto op1 = index_assign->GetOp1();
const auto& t1 = op1->GetType();
auto op3 = index_assign->GetOp3();
const auto& t3 = op3->GetType();
auto lhs = op1->AsNameExpr();
auto lt = lhs->GetType();
auto indexes_expr = index_assign->GetOp2()->AsListExpr();
auto indexes = indexes_expr->Exprs();
if ( indexes.length() > 1 ) { // Vector slice assignment.
ASSERT(op1->Tag() == EXPR_NAME);
ASSERT(op3->Tag() == EXPR_NAME);
ASSERT(t1->Tag() == TYPE_VECTOR);
ASSERT(t3->Tag() == TYPE_VECTOR);
auto z = GenInst(OP_VECTOR_SLICE_ASSIGN_VV, lhs, op3->AsNameExpr());
z.aux = InternalBuildVals(indexes_expr);
return AddInst(z);
}
const auto& yt1 = t1->Yield();
auto any_vec = yt1->Tag() == TYPE_VOID || yt1->Tag() == TYPE_ANY;
auto any_val = IsAny(t3);
auto op2 = indexes[0];
if ( op2->Tag() == EXPR_CONST && op3->Tag() == EXPR_CONST ) {
// Turn into a VVC assignment by assigning the index to
// a temporary.
auto c = op2->AsConstExpr();
auto tmp = TempForConst(c);
auto zop = any_vec ? OP_ANY_VECTOR_ELEM_ASSIGN_VVC : OP_VECTOR_ELEM_ASSIGN_VVC;
return AddInst(ZInstI(zop, Frame1Slot(lhs, zop), tmp, op3->AsConstExpr()));
}
if ( op2->Tag() == EXPR_NAME ) {
auto n2 = op2->AsNameExpr();
ZAMStmt inst(0);
if ( op3->Tag() == EXPR_NAME ) {
auto n3 = op3->AsNameExpr();
if ( any_vec )
inst = Any_Vector_Elem_AssignVVV(lhs, n2, n3);
else if ( any_val )
inst = Vector_Elem_Assign_AnyVVV(lhs, n2, n3);
else
inst = Vector_Elem_AssignVVV(lhs, n2, n3);
}
else {
auto c3 = op3->AsConstExpr();
if ( any_vec )
inst = Any_Vector_Elem_AssignVVC(lhs, n2, c3);
else
inst = Vector_Elem_AssignVVC(lhs, n2, c3);
}
TopMainInst()->t = t3;
return inst;
}
auto c2 = op2->AsConstExpr();
auto n3 = op3->AsNameExpr();
auto index = c2->Value()->AsCount();
ZAMStmt inst;
if ( any_vec )
inst = Any_Vector_Elem_AssignVVi(lhs, n3, index);
else if ( any_val )
inst = Vector_Elem_Assign_AnyVVi(lhs, n3, index);
else
inst = Vector_Elem_AssignVVi(lhs, n3, index);
TopMainInst()->t = t3;
return inst;
}
const ZAMStmt ZAMCompiler::AssignTableElem(const Expr* e) {
auto index_assign = e->AsIndexAssignExpr();
auto op1 = index_assign->GetOp1()->AsNameExpr();
auto op2 = index_assign->GetOp2()->AsListExpr();
auto op3 = index_assign->GetOp3();
ZInstI z;
if ( op3->Tag() == EXPR_NAME )
z = GenInst(OP_TABLE_ELEM_ASSIGN_VV, op1, op3->AsNameExpr());
else
z = GenInst(OP_TABLE_ELEM_ASSIGN_VC, op1, op3->AsConstExpr());
z.aux = InternalBuildVals(op2);
z.t = op3->GetType();
if ( pfs->HasSideEffects(SideEffectsOp::WRITE, op1->GetType()) )
z.aux->can_change_non_locals = true;
return AddInst(z);
}
const ZAMStmt ZAMCompiler::Call(const ExprStmt* e) {
auto c = cast_intrusive<CallExpr>(e->StmtExprPtr());
if ( IsZAM_BuiltIn(c.get()) ) {
auto ret = LastInst();
insts1.back()->call_expr = c;
return ret;
}
return DoCall(e->StmtExpr()->AsCallExpr(), nullptr);
}
const ZAMStmt ZAMCompiler::AssignToCall(const ExprStmt* e) {
auto assign = e->StmtExpr()->AsAssignExpr();
auto call = cast_intrusive<CallExpr>(assign->GetOp2());
if ( IsZAM_BuiltIn(e->StmtExpr()) ) {
auto ret = LastInst();
insts1.back()->call_expr = call;
return ret;
}
auto n = assign->GetOp1()->AsRefExpr()->GetOp1()->AsNameExpr();
return DoCall(call.get(), n);
}
const ZAMStmt ZAMCompiler::DoCall(const CallExpr* c, const NameExpr* n) {
auto func = c->Func()->AsNameExpr();
auto func_id = func->IdPtr();
auto& args = c->Args()->Exprs();
int nargs = args.length();
int call_case = nargs;
bool indirect = ! func_id->IsGlobal() || ! func_id->GetVal();
bool in_when = c->IsInWhen();
if ( indirect || in_when )
call_case = -1; // force default of some flavor of CallN
auto nt = n ? n->GetType()->Tag() : TYPE_VOID;
auto n_slot = n ? Frame1Slot(n, OP1_WRITE) : -1;
ZInstI z;
if ( call_case == 0 ) {
if ( n )
z = ZInstI(AssignmentFlavor(OP_CALL0_V, nt), n_slot);
else
z = ZInstI(OP_CALL0_X);
}
else if ( call_case == 1 ) {
auto arg0 = args[0];
auto n0 = arg0->Tag() == EXPR_NAME ? arg0->AsNameExpr() : nullptr;
auto c0 = arg0->Tag() == EXPR_CONST ? arg0->AsConstExpr() : nullptr;
if ( n ) {
if ( n0 )
z = ZInstI(AssignmentFlavor(OP_CALL1_VV, nt), n_slot, FrameSlot(n0));
else {
ASSERT(c0);
z = ZInstI(AssignmentFlavor(OP_CALL1_VC, nt), n_slot, c0);
}
}
else {
if ( n0 )
z = ZInstI(OP_CALL1_V, FrameSlot(n0));
else {
ASSERT(c0);
z = ZInstI(OP_CALL1_C, c0);
}
}
z.t = arg0->GetType();
}
else {
auto aux = new ZInstAux(nargs);
for ( int i = 0; i < nargs; ++i ) {
auto ai = args[i];
auto ai_t = ai->GetType();
if ( ai->Tag() == EXPR_NAME )
aux->Add(i, FrameSlot(ai->AsNameExpr()), ai_t);
else
aux->Add(i, ai->AsConstExpr()->ValuePtr());
}
ZOp op;
switch ( call_case ) {
case 2: op = n ? OP_CALL2_V : OP_CALL2_X; break;
case 3: op = n ? OP_CALL3_V : OP_CALL3_X; break;
case 4: op = n ? OP_CALL4_V : OP_CALL4_X; break;
case 5: op = n ? OP_CALL5_V : OP_CALL5_X; break;
default:
if ( in_when ) {
if ( indirect )
op = OP_WHENINDCALLN_VV;
else
op = OP_WHENCALLN_V;
}
else if ( indirect ) {
if ( func_id->IsGlobal() )
op = n ? OP_INDCALLN_V : OP_INDCALLN_X;
else
op = n ? OP_LOCAL_INDCALLN_VV : OP_LOCAL_INDCALLN_V;
}
else
op = n ? OP_CALLN_V : OP_CALLN_X;
break;
}
if ( n ) {
if ( ! in_when )
op = AssignmentFlavor(op, nt);
auto n_slot = Frame1Slot(n, OP1_WRITE);
if ( indirect ) {
if ( func_id->IsGlobal() ) {
z = ZInstI(op, n_slot);
z.op_type = OP_V;
}
else {
z = ZInstI(op, n_slot, FrameSlot(func));
z.op_type = OP_VV;
}
}
else {
z = ZInstI(op, n_slot);
z.op_type = OP_V;
}
}
else {
if ( indirect && ! func_id->IsGlobal() ) {
z = ZInstI(op, FrameSlot(func));
z.op_type = OP_V;
}
else {
z = ZInstI(op);
z.op_type = OP_X;
}
}
z.aux = aux;
}
if ( ! z.aux )
z.aux = new ZInstAux(0);
if ( indirect )
z.aux->can_change_non_locals = true;
else {
IDSet non_local_ids;
TypeSet aggrs;
bool is_unknown = false;
auto resolved = pfs->GetCallSideEffects(func, non_local_ids, aggrs, is_unknown);
ASSERT(resolved);
if ( is_unknown || ! non_local_ids.empty() || ! aggrs.empty() )
z.aux->can_change_non_locals = true;
}
z.call_expr = {NewRef{}, const_cast<CallExpr*>(c)};
if ( in_when )
z.SetType(n->GetType());
if ( ! indirect || func_id->IsGlobal() ) {
z.aux->id_val = func_id;
if ( ! indirect )
z.func = func_id->GetVal()->AsFunc();
}
return AddInst(z);
}
const ZAMStmt ZAMCompiler::ConstructTable(const NameExpr* n, const Expr* e) {
auto con = e->GetOp1()->AsListExpr();
auto tt = cast_intrusive<TableType>(n->GetType());
auto width = tt->GetIndices()->GetTypes().size();
auto z = GenInst(OP_CONSTRUCT_TABLE_VV, n, width);
z.aux = InternalBuildVals(con, width + 1);
z.t = tt;
z.attrs = e->AsTableConstructorExpr()->GetAttrs();
auto zstmt = AddInst(z);
auto def_attr = z.attrs ? z.attrs->Find(ATTR_DEFAULT) : nullptr;
if ( ! def_attr || def_attr->GetExpr()->Tag() != EXPR_LAMBDA )
return zstmt;
auto def_lambda = def_attr->GetExpr()->AsLambdaExpr();
auto dl_t = def_lambda->GetType()->AsFuncType();
auto& captures = dl_t->GetCaptures();
if ( ! captures )
return zstmt;
// What a pain. The table's default value is a lambda that has
// captures. The semantics of this are that the captures are
// evaluated at table-construction time. We need to build the
// lambda and assign it as the table's default.
auto slot = NewSlot(true); // since func_val's are managed
(void)BuildLambda(slot, def_lambda);
z = GenInst(OP_SET_TABLE_DEFAULT_LAMBDA_VV, n, slot);
z.op_type = OP_VV;
z.t = def_lambda->GetType();
return AddInst(z);
}
const ZAMStmt ZAMCompiler::ConstructSet(const NameExpr* n, const Expr* e) {
auto con = e->GetOp1()->AsListExpr();
auto tt = n->GetType()->AsTableType();
auto width = tt->GetIndices()->GetTypes().size();
auto z = GenInst(OP_CONSTRUCT_SET_VV, n, width);
z.aux = InternalBuildVals(con, width);
z.t = e->GetType();
z.attrs = e->AsSetConstructorExpr()->GetAttrs();
return AddInst(z);
}
const ZAMStmt ZAMCompiler::ConstructRecord(const NameExpr* n, const Expr* e) {
auto rc = e->AsRecordConstructorExpr();
auto rt = e->GetType()->AsRecordType();
auto aux = InternalBuildVals(rc->Op().get());
// Note that we set the vector to the full size of the record being
// constructed, *not* the size of any map (which could be smaller).
// This is because we want to provide the vector directly to the
// constructor.
aux->zvec.resize(rt->NumFields());
if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, e->GetType()) )
aux->can_change_non_locals = true;
ZOp op;
const auto& map = rc->Map();
if ( map ) {
aux->map = *map;
auto fi = std::make_unique<std::vector<std::pair<int, std::shared_ptr<detail::FieldInit>>>>();
// Populate the field inits as needed.
for ( auto& c : rt->CreationInits() ) {
bool seen = false;
for ( auto r : *map )
if ( c.first == r ) {
// Superseded by a constructor element;
seen = true;
break;
}
if ( ! seen )
// Need to generate field dynamically.
fi->push_back(c);
}
if ( fi->empty() )
op = OP_CONSTRUCT_KNOWN_RECORD_V;
else {
op = OP_CONSTRUCT_KNOWN_RECORD_WITH_INITS_V;
aux->field_inits = std::move(fi);
}
}
else
op = OP_CONSTRUCT_DIRECT_RECORD_V;
ZInstI z = GenInst(op, n);
z.aux = aux;
z.t = e->GetType();
auto inst = AddInst(z);
// If one of the initialization values is an unspecified vector (which
// in general we can't know until run-time) then we'll need to
// "concretize" it. We first see whether this is a possibility, since
// it usually isn't, by counting up how many of the record fields are
// vectors.
std::vector<int> vector_fields; // holds indices of the vector fields
for ( int i = 0; i < z.aux->n; ++i ) {
auto field_ind = map ? (*map)[i] : i;
auto& field_t = rt->GetFieldType(field_ind);
if ( field_t->Tag() == TYPE_VECTOR && field_t->Yield()->Tag() != TYPE_ANY )
vector_fields.push_back(field_ind);
}
if ( vector_fields.empty() )
// Common case of no vector fields, we're done.
return inst;
// Need to add a separate instruction for concretizing the fields.
z = GenInst(OP_CONCRETIZE_VECTOR_FIELDS_V, n);
z.t = e->GetType();
int nf = static_cast<int>(vector_fields.size());
z.aux = new ZInstAux(nf);
z.aux->elems_has_slots = false; // we're storing field offsets, not slots
for ( int i = 0; i < nf; ++i )
z.aux->Add(i, vector_fields[i]);
return AddInst(z);
}
const ZAMStmt ZAMCompiler::ConstructVector(const NameExpr* n, const Expr* e) {
auto con = e->GetOp1()->AsListExpr();
auto z = GenInst(OP_CONSTRUCT_VECTOR_V, n);
z.aux = InternalBuildVals(con);
z.t = e->GetType();
return AddInst(z);
}
const ZAMStmt ZAMCompiler::ArithCoerce(const NameExpr* n, const Expr* e) {
auto nt = n->GetType();
auto nt_is_vec = nt->Tag() == TYPE_VECTOR;
auto op = e->GetOp1();
auto op_t = op->GetType();
auto op_is_vec = op_t->Tag() == TYPE_VECTOR;
auto e_t = e->GetType();
auto et_is_vec = e_t->Tag() == TYPE_VECTOR;
if ( nt_is_vec || op_is_vec || et_is_vec ) {
if ( ! (nt_is_vec && op_is_vec && et_is_vec) )
reporter->InternalError("vector confusion compiling coercion");
op_t = op_t->AsVectorType()->Yield();
e_t = e_t->AsVectorType()->Yield();
}
auto targ_it = e_t->InternalType();
auto op_it = op_t->InternalType();
if ( op_it == targ_it )
reporter->InternalError("coercion wasn't folded");
if ( op->Tag() != EXPR_NAME )
reporter->InternalError("coercion wasn't folded");
ZOp a;
switch ( targ_it ) {
case TYPE_INTERNAL_DOUBLE: {
if ( op_it == TYPE_INTERNAL_INT )
a = nt_is_vec ? OP_COERCE_DI_VEC_VV : OP_COERCE_DI_VV;
else
a = nt_is_vec ? OP_COERCE_DU_VEC_VV : OP_COERCE_DU_VV;
break;
}
case TYPE_INTERNAL_INT: {
if ( op_it == TYPE_INTERNAL_UNSIGNED )
a = nt_is_vec ? OP_COERCE_IU_VEC_VV : OP_COERCE_IU_VV;
else
a = nt_is_vec ? OP_COERCE_ID_VEC_VV : OP_COERCE_ID_VV;
break;
}
case TYPE_INTERNAL_UNSIGNED: {
if ( op_it == TYPE_INTERNAL_INT )
a = nt_is_vec ? OP_COERCE_UI_VEC_VV : OP_COERCE_UI_VV;
else
a = nt_is_vec ? OP_COERCE_UD_VEC_VV : OP_COERCE_UD_VV;
break;
}
default: reporter->InternalError("bad target internal type in coercion");
}
return AddInst(GenInst(a, n, op->AsNameExpr()));
}
const ZAMStmt ZAMCompiler::RecordCoerce(const NameExpr* n, const Expr* e) {
auto r = e->AsRecordCoerceExpr();
auto op = r->GetOp1()->AsNameExpr();
int op_slot = FrameSlot(op);
auto zop = OP_RECORD_COERCE_VV;
ZInstI z(zop, Frame1Slot(n, zop), op_slot);
z.SetType(e->GetType());
z.op_type = OP_VV;
auto map = r->Map();
auto map_size = map.size();
z.aux = new ZInstAux(map_size);
z.aux->map = map;
for ( auto i = 0U; i < map_size; ++i )
z.aux->Add(i, map[i], nullptr);
// Mark the integer entries in z.aux as not being frame slots as usual.
z.aux->elems_has_slots = false;
if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, e->GetType()) )
z.aux->can_change_non_locals = true;
return AddInst(z);
}
const ZAMStmt ZAMCompiler::TableCoerce(const NameExpr* n, const Expr* e) {
auto op = e->GetOp1()->AsNameExpr();
int op_slot = FrameSlot(op);
auto zop = OP_TABLE_COERCE_VV;
ZInstI z(zop, Frame1Slot(n, zop), op_slot);
z.SetType(e->GetType());
return AddInst(z);
}
const ZAMStmt ZAMCompiler::VectorCoerce(const NameExpr* n, const Expr* e) {
auto op = e->GetOp1()->AsNameExpr();
int op_slot = FrameSlot(op);
auto zop = OP_VECTOR_COERCE_VV;
ZInstI z(zop, Frame1Slot(n, zop), op_slot);
z.SetType(e->GetType());
return AddInst(z);
}
const ZAMStmt ZAMCompiler::Is(const NameExpr* n, const Expr* e) {
auto is = e->AsIsExpr();
auto op = e->GetOp1()->AsNameExpr();
int op_slot = FrameSlot(op);
ZInstI z(OP_IS_VV, Frame1Slot(n, OP_IS_VV), op_slot);
z.t2 = op->GetType();
z.SetType(is->TestType());
return AddInst(z);
}
} // namespace zeek::detail