mirror of
https://github.com/zeek/zeek.git
synced 2025-10-07 00:58:19 +00:00
578 lines
15 KiB
C++
578 lines
15 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek/script_opt/ZAM/ZInst.h"
|
|
|
|
#include "zeek/Desc.h"
|
|
#include "zeek/Func.h"
|
|
#include "zeek/Reporter.h"
|
|
|
|
using std::string;
|
|
|
|
namespace zeek::detail {
|
|
|
|
void ZInst::Dump(zeek_uint_t inst_num, const FrameReMap* mappings) const {
|
|
// printf("v%d ", n);
|
|
|
|
auto id1 = VName(1, inst_num, mappings);
|
|
auto id2 = VName(2, inst_num, mappings);
|
|
auto id3 = VName(3, inst_num, mappings);
|
|
auto id4 = VName(4, inst_num, mappings);
|
|
|
|
Dump(id1, id2, id3, id4);
|
|
}
|
|
|
|
void ZInst::Dump(const string& id1, const string& id2, const string& id3, const string& id4) const {
|
|
printf("%s ", ZOP_name(op));
|
|
// printf("(%s) ", op_type_name(op_type));
|
|
if ( t && 0 )
|
|
printf("(%s) ", type_name(t->Tag()));
|
|
|
|
switch ( op_type ) {
|
|
case OP_X: break;
|
|
|
|
case OP_V: printf("%s", id1.c_str()); break;
|
|
|
|
case OP_VV: printf("%s, %s", id1.c_str(), id2.c_str()); break;
|
|
|
|
case OP_VVV: printf("%s, %s, %s", id1.c_str(), id2.c_str(), id3.c_str()); break;
|
|
|
|
case OP_VVVV: printf("%s, %s, %s, %s", id1.c_str(), id2.c_str(), id3.c_str(), id4.c_str()); break;
|
|
|
|
case OP_VVVC: printf("%s, %s, %s, %s", id1.c_str(), id2.c_str(), id3.c_str(), ConstDump().c_str()); break;
|
|
|
|
case OP_C: printf("%s", ConstDump().c_str()); break;
|
|
|
|
case OP_VC: printf("%s, %s", id1.c_str(), ConstDump().c_str()); break;
|
|
|
|
case OP_VVC: printf("%s, %s, %s", id1.c_str(), id2.c_str(), ConstDump().c_str()); break;
|
|
|
|
case OP_V_I1: printf("%d", v1); break;
|
|
|
|
case OP_VC_I1: printf("%d %s", v1, ConstDump().c_str()); break;
|
|
|
|
case OP_VV_FRAME: printf("%s, interpreter frame[%d]", id1.c_str(), v2); break;
|
|
|
|
case OP_VV_I2: printf("%s, %d", id1.c_str(), v2); break;
|
|
|
|
case OP_VV_I1_I2: printf("%d, %d", v1, v2); break;
|
|
|
|
case OP_VVC_I2: printf("%s, %d, %s", id1.c_str(), v2, ConstDump().c_str()); break;
|
|
|
|
case OP_VVV_I3: printf("%s, %s, %d", id1.c_str(), id2.c_str(), v3); break;
|
|
|
|
case OP_VVV_I2_I3: printf("%s, %d, %d", id1.c_str(), v2, v3); break;
|
|
|
|
case OP_VVVV_I4: printf("%s, %s, %s, %d", id1.c_str(), id2.c_str(), id3.c_str(), v4); break;
|
|
|
|
case OP_VVVV_I3_I4: printf("%s, %s, %d, %d", id1.c_str(), id2.c_str(), v3, v4); break;
|
|
|
|
case OP_VVVV_I2_I3_I4: printf("%s, %d, %d, %d", id1.c_str(), v2, v3, v4); break;
|
|
|
|
case OP_VVVC_I3: printf("%s, %s, %d, %s", id1.c_str(), id2.c_str(), v3, ConstDump().c_str()); break;
|
|
|
|
case OP_VVVC_I2_I3: printf("%s, %d, %d, %s", id1.c_str(), v2, v3, ConstDump().c_str()); break;
|
|
|
|
case OP_VVVC_I1_I2_I3: printf("%d, %d, %d, %s", v1, v2, v3, ConstDump().c_str()); break;
|
|
}
|
|
|
|
if ( func )
|
|
printf(" (func %s)", func->Name());
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
int ZInst::NumFrameSlots() const {
|
|
switch ( op_type ) {
|
|
case OP_X:
|
|
case OP_C:
|
|
case OP_V_I1:
|
|
case OP_VC_I1:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVVC_I1_I2_I3: return 0;
|
|
|
|
case OP_V:
|
|
case OP_VC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVV_I2_I3_I4: return 1;
|
|
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VVV_I3:
|
|
case OP_VVVC_I3:
|
|
case OP_VVVV_I3_I4: return 2;
|
|
|
|
case OP_VVV:
|
|
case OP_VVVC:
|
|
case OP_VVVV_I4: return 3;
|
|
|
|
case OP_VVVV: return 4;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int ZInst::NumSlots() const {
|
|
switch ( op_type ) {
|
|
case OP_C:
|
|
case OP_X: return 0;
|
|
|
|
case OP_V:
|
|
case OP_V_I1:
|
|
case OP_VC:
|
|
case OP_VC_I1: return 1;
|
|
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VV_I1_I2: return 2;
|
|
|
|
case OP_VVV:
|
|
case OP_VVV_I3:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC:
|
|
case OP_VVVC_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVC_I1_I2_I3: return 3;
|
|
|
|
case OP_VVVV:
|
|
case OP_VVVV_I4:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVV_I2_I3_I4: return 4;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
string ZInst::VName(int n, zeek_uint_t inst_num, const FrameReMap* mappings) const {
|
|
if ( n > NumFrameSlots() )
|
|
return "";
|
|
|
|
int slot = n == 1 ? v1 : (n == 2 ? v2 : (n == 3 ? v3 : v4));
|
|
|
|
if ( slot < 0 )
|
|
return "<special>";
|
|
|
|
// Find which identifier manifests at this instruction.
|
|
ASSERT(slot >= 0 && static_cast<zeek_uint_t>(slot) < mappings->size());
|
|
|
|
auto& map = (*mappings)[slot];
|
|
|
|
unsigned int i;
|
|
for ( i = 0; i < map.id_start.size(); ++i ) {
|
|
// If the slot is right at the boundary between two identifiers, then
|
|
// it matters whether this is an assigned slot (starts right here) vs.
|
|
// not assigned (ignore change right at the boundary and stick with
|
|
// older value).
|
|
auto target_inst = AssignsToSlot(n) ? inst_num + 1 : inst_num;
|
|
if ( map.id_start[i] >= target_inst )
|
|
// Went too far.
|
|
break;
|
|
}
|
|
|
|
if ( i < map.id_start.size() ) {
|
|
ASSERT(i > 0);
|
|
}
|
|
|
|
auto id = map.names.empty() ? map.ids[i - 1]->Name() : map.names[i - 1];
|
|
|
|
return util::fmt("%d (%s)", slot, id);
|
|
}
|
|
|
|
ValPtr ZInst::ConstVal() const {
|
|
switch ( op_type ) {
|
|
case OP_C:
|
|
case OP_VC:
|
|
case OP_VC_I1:
|
|
case OP_VVC:
|
|
case OP_VVC_I2:
|
|
case OP_VVVC:
|
|
case OP_VVVC_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVC_I1_I2_I3: return c.ToVal(t);
|
|
|
|
case OP_X:
|
|
case OP_V:
|
|
case OP_VV:
|
|
case OP_VVV:
|
|
case OP_VVVV:
|
|
case OP_V_I1:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVV_I3:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVV_I4:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVV_I2_I3_I4: return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool ZInst::IsLoopIterationAdvancement() const {
|
|
switch ( op ) {
|
|
case OP_NEXT_TABLE_ITER_VV:
|
|
case OP_NEXT_TABLE_ITER_NO_VARS_VV:
|
|
case OP_NEXT_TABLE_ITER_VAL_VAR_VVV:
|
|
case OP_NEXT_TABLE_ITER_VAL_VAR_NO_VARS_VVV:
|
|
case OP_NEXT_VECTOR_ITER_VVV:
|
|
case OP_NEXT_VECTOR_BLANK_ITER_VV:
|
|
case OP_NEXT_VECTOR_ITER_VAL_VAR_VVVV:
|
|
case OP_NEXT_VECTOR_BLANK_ITER_VAL_VAR_VVV:
|
|
case OP_NEXT_STRING_ITER_VVV:
|
|
case OP_NEXT_STRING_BLANK_ITER_VV: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool ZInst::AssignsToSlot1() const {
|
|
switch ( op_type ) {
|
|
case OP_X:
|
|
case OP_C:
|
|
case OP_V_I1:
|
|
case OP_VC_I1:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVVC_I1_I2_I3: return false;
|
|
|
|
// We use this ginormous set of cases rather than "default" so
|
|
// that when we add a new operand type, we have to consider
|
|
// its behavior here. (Same for many of the other switch's
|
|
// used for ZInst/ZinstI.)
|
|
case OP_V:
|
|
case OP_VC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVV_I2_I3_I4:
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VVV_I3:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVC_I3:
|
|
case OP_VVV:
|
|
case OP_VVVC:
|
|
case OP_VVVV_I4:
|
|
case OP_VVVV: auto fl = op1_flavor[op]; return fl == OP1_WRITE || fl == OP1_READ_WRITE;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ZInst::AssignsToSlot(int slot) const {
|
|
switch ( op ) {
|
|
case OP_NEXT_VECTOR_ITER_VAL_VAR_VVVV: return slot == 1 || slot == 2;
|
|
|
|
default: return slot == 1 && AssignsToSlot1();
|
|
}
|
|
}
|
|
|
|
string ZInst::ConstDump() const {
|
|
auto v = ConstVal();
|
|
|
|
ODesc d;
|
|
|
|
d.Clear();
|
|
v->Describe(&d);
|
|
|
|
return d.Description();
|
|
}
|
|
|
|
void ZInstI::Dump(const FrameMap* frame_ids, const FrameReMap* remappings) const {
|
|
int n = NumFrameSlots();
|
|
// printf("v%d ", n);
|
|
|
|
auto id1 = VName(1, frame_ids, remappings);
|
|
auto id2 = VName(2, frame_ids, remappings);
|
|
auto id3 = VName(3, frame_ids, remappings);
|
|
auto id4 = VName(4, frame_ids, remappings);
|
|
|
|
ZInst::Dump(id1, id2, id3, id4);
|
|
}
|
|
|
|
string ZInstI::VName(int n, const FrameMap* frame_ids, const FrameReMap* remappings) const {
|
|
if ( n > NumFrameSlots() )
|
|
return "";
|
|
|
|
int slot = n == 1 ? v1 : (n == 2 ? v2 : (n == 3 ? v3 : v4));
|
|
|
|
if ( slot < 0 )
|
|
return "<special>";
|
|
|
|
const ID* id;
|
|
|
|
if ( remappings && live ) { // Find which identifier manifests at this instruction.
|
|
ASSERT(slot >= 0 && static_cast<zeek_uint_t>(slot) < remappings->size());
|
|
|
|
auto& map = (*remappings)[slot];
|
|
|
|
unsigned int i;
|
|
auto inst_num_u = static_cast<zeek_uint_t>(inst_num);
|
|
for ( i = 0; i < map.id_start.size(); ++i ) {
|
|
// See discussion for ZInst::VName, though this is
|
|
// a tad different since we have the general notion
|
|
// of AssignsToSlot().
|
|
if ( AssignsToSlot(n) ) {
|
|
if ( map.id_start[i] > inst_num_u )
|
|
break;
|
|
}
|
|
|
|
else if ( map.id_start[i] >= inst_num_u )
|
|
// Went too far.
|
|
break;
|
|
}
|
|
|
|
if ( i < map.id_start.size() ) {
|
|
ASSERT(i > 0);
|
|
}
|
|
|
|
// For ZInstI's, map.ids is always populated.
|
|
id = map.ids[i - 1];
|
|
}
|
|
|
|
else
|
|
id = (*frame_ids)[slot];
|
|
|
|
return util::fmt("%d (%s)", slot, id->Name());
|
|
}
|
|
|
|
bool ZInstI::DoesNotContinue() const {
|
|
switch ( op ) {
|
|
case OP_GOTO_V:
|
|
case OP_HOOK_BREAK_X:
|
|
case OP_RETURN_C:
|
|
case OP_RETURN_V:
|
|
case OP_RETURN_X: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool ZInstI::IsDirectAssignment() const {
|
|
if ( op_type != OP_VV )
|
|
return false;
|
|
|
|
switch ( op ) {
|
|
case OP_ASSIGN_VV_A:
|
|
case OP_ASSIGN_VV_D:
|
|
case OP_ASSIGN_VV_F:
|
|
case OP_ASSIGN_VV_I:
|
|
case OP_ASSIGN_VV_L:
|
|
case OP_ASSIGN_VV_N:
|
|
case OP_ASSIGN_VV_O:
|
|
case OP_ASSIGN_VV_P:
|
|
case OP_ASSIGN_VV_R:
|
|
case OP_ASSIGN_VV_S:
|
|
case OP_ASSIGN_VV_T:
|
|
case OP_ASSIGN_VV_U:
|
|
case OP_ASSIGN_VV_V:
|
|
case OP_ASSIGN_VV_a:
|
|
case OP_ASSIGN_VV_f:
|
|
case OP_ASSIGN_VV_t:
|
|
case OP_ASSIGN_VV: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool ZInstI::HasCaptures() const {
|
|
switch ( op ) {
|
|
case OP_LAMBDA_VV:
|
|
case OP_WHEN_V:
|
|
case OP_WHEN_TIMEOUT_VV:
|
|
case OP_WHEN_TIMEOUT_VC: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool ZInstI::HasSideEffects() const { return op_side_effects[op]; }
|
|
|
|
bool ZInstI::UsesSlot(int slot) const {
|
|
auto fl = op1_flavor[op];
|
|
auto v1_relevant = fl == OP1_READ || fl == OP1_READ_WRITE;
|
|
auto v1_match = v1_relevant && v1 == slot;
|
|
|
|
switch ( op_type ) {
|
|
case OP_X:
|
|
case OP_C:
|
|
case OP_V_I1:
|
|
case OP_VC_I1:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVVC_I1_I2_I3: return false;
|
|
|
|
case OP_V:
|
|
case OP_VC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVV_I2_I3_I4: return v1_match;
|
|
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VVV_I3:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVC_I3: return v1_match || v2 == slot;
|
|
|
|
case OP_VVV:
|
|
case OP_VVVC:
|
|
case OP_VVVV_I4: return v1_match || v2 == slot || v3 == slot;
|
|
|
|
case OP_VVVV: return v1_match || v2 == slot || v3 == slot || v4 == slot;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ZInstI::UsesSlots(int& s1, int& s2, int& s3, int& s4) const {
|
|
s1 = s2 = s3 = s4 = -1;
|
|
|
|
auto fl = op1_flavor[op];
|
|
auto v1_relevant = fl == OP1_READ || fl == OP1_READ_WRITE;
|
|
|
|
switch ( op_type ) {
|
|
case OP_X:
|
|
case OP_C:
|
|
case OP_V_I1:
|
|
case OP_VC_I1:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVVC_I1_I2_I3: return false;
|
|
|
|
case OP_V:
|
|
case OP_VC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVV_I2_I3_I4:
|
|
if ( ! v1_relevant )
|
|
return false;
|
|
|
|
s1 = v1;
|
|
return true;
|
|
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VVV_I3:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVC_I3:
|
|
s1 = v2;
|
|
|
|
if ( v1_relevant )
|
|
s2 = v1;
|
|
|
|
return true;
|
|
|
|
case OP_VVV:
|
|
case OP_VVVC:
|
|
case OP_VVVV_I4:
|
|
s1 = v2;
|
|
s2 = v3;
|
|
|
|
if ( v1_relevant )
|
|
s3 = v1;
|
|
|
|
return true;
|
|
|
|
case OP_VVVV:
|
|
s1 = v2;
|
|
s2 = v3;
|
|
s3 = v4;
|
|
|
|
if ( v1_relevant )
|
|
s4 = v1;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ZInstI::UpdateSlots(std::vector<int>& slot_mapping) {
|
|
switch ( op_type ) {
|
|
case OP_X:
|
|
case OP_C:
|
|
case OP_V_I1:
|
|
case OP_VC_I1:
|
|
case OP_VV_I1_I2:
|
|
case OP_VVVC_I1_I2_I3: return; // so we don't do any v1 remapping.
|
|
|
|
case OP_V:
|
|
case OP_VC:
|
|
case OP_VV_FRAME:
|
|
case OP_VV_I2:
|
|
case OP_VVC_I2:
|
|
case OP_VVV_I2_I3:
|
|
case OP_VVVC_I2_I3:
|
|
case OP_VVVV_I2_I3_I4: break;
|
|
|
|
case OP_VV:
|
|
case OP_VVC:
|
|
case OP_VVV_I3:
|
|
case OP_VVVV_I3_I4:
|
|
case OP_VVVC_I3: v2 = slot_mapping[v2]; break;
|
|
|
|
case OP_VVV:
|
|
case OP_VVVC:
|
|
case OP_VVVV_I4:
|
|
v2 = slot_mapping[v2];
|
|
v3 = slot_mapping[v3];
|
|
break;
|
|
|
|
case OP_VVVV:
|
|
v2 = slot_mapping[v2];
|
|
v3 = slot_mapping[v3];
|
|
v4 = slot_mapping[v4];
|
|
break;
|
|
}
|
|
|
|
// Note, unlike for UsesSlots() we do *not* include OP1_READ_WRITE
|
|
// here, because such instructions will already have v1 remapped
|
|
// given it's an assignment target.
|
|
if ( op1_flavor[op] == OP1_READ && v1 >= 0 )
|
|
v1 = slot_mapping[v1];
|
|
}
|
|
|
|
bool ZInstI::IsGlobalLoad() const {
|
|
if ( op == OP_LOAD_GLOBAL_TYPE_VV )
|
|
// These don't have flavors.
|
|
return true;
|
|
|
|
static std::unordered_set<ZOp> global_ops;
|
|
|
|
if ( global_ops.empty() ) { // Initialize the set.
|
|
for ( int t = 0; t < NUM_TYPES; ++t ) {
|
|
TypeTag tag = TypeTag(t);
|
|
ZOp global_op_flavor = AssignmentFlavor(OP_LOAD_GLOBAL_VV, tag, false);
|
|
|
|
if ( global_op_flavor != OP_NOP )
|
|
global_ops.insert(global_op_flavor);
|
|
}
|
|
}
|
|
|
|
return global_ops.count(op) > 0;
|
|
}
|
|
|
|
bool ZInstI::IsCaptureLoad() const { return op == OP_LOAD_CAPTURE_VV || op == OP_LOAD_MANAGED_CAPTURE_VV; }
|
|
|
|
void ZInstI::InitConst(const ConstExpr* ce) {
|
|
auto v = ce->ValuePtr();
|
|
t = ce->GetType();
|
|
c = ZVal(v, t);
|
|
|
|
if ( ZAM_error )
|
|
reporter->InternalError("bad value compiling code");
|
|
}
|
|
|
|
} // namespace zeek::detail
|