Filter: specialize 'and' and 'or' ops.
[lttng-ust.git] / liblttng-ust / lttng-filter.c
index 7fe2a7b5d5642cb3ac92f0de76589448956ea2c6..dffcca5fafaf41bb249f2292e315cb6fe3287692 100644 (file)
@@ -152,6 +152,10 @@ static const char *opnames[] = {
        /* logical */
        [ FILTER_OP_AND ] = "AND",
        [ FILTER_OP_OR ] = "OR",
+       [ FILTER_OP_AND_S64 ] = "AND_S64",
+       [ FILTER_OP_OR_S64 ] = "OR_S64",
+       [ FILTER_OP_AND_DOUBLE ] = "AND_DOUBLE",
+       [ FILTER_OP_OR_DOUBLE ] = "OR_DOUBLE",
 
        /* load */
        [ FILTER_OP_LOAD_FIELD_REF ] = "LOAD_FIELD_REF",
@@ -264,10 +268,12 @@ int lttng_filter_false(void *filter_data,
        return 0;
 }
 
-#define INTERPRETER_USE_SWITCH
-
 #ifdef INTERPRETER_USE_SWITCH
 
+/*
+ * Fallback for compilers that do not support taking address of labels.
+ */
+
 #define START_OP       \
        start_pc = &bytecode->data[0]; \
        for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; \
@@ -286,7 +292,25 @@ int lttng_filter_false(void *filter_data,
 
 #else
 
-#define OP(name)       
+/*
+ * Dispatch-table based interpreter.
+ */
+
+#define START_OP                                       \
+       start_pc = &bytecode->data[0];                  \
+       pc = next_pc = start_pc;                        \
+       if (unlikely(pc - start_pc >= bytecode->len))   \
+               goto end;                               \
+       goto *dispatch[*(filter_opcode_t *) pc];
+
+#define OP(name)                                       \
+LABEL_##name
+
+#define PO                                             \
+               pc = next_pc;                           \
+               goto *dispatch[*(filter_opcode_t *) pc];
+
+#define END_OP
 
 #endif
 
@@ -301,7 +325,7 @@ int lttng_filter_interpret_bytecode(void *filter_data,
        struct reg reg[NR_REG];
 #ifndef INTERPRETER_USE_SWITCH
        static void *dispatch[NR_FILTER_OPS] = {
-               [ FILTER_OP_UNKNOWN ] = &&LABEL_FILTER_OP_UNKNOWN = 0,
+               [ FILTER_OP_UNKNOWN ] = &&LABEL_FILTER_OP_UNKNOWN,
 
                [ FILTER_OP_RETURN ] = &&LABEL_FILTER_OP_RETURN,
 
@@ -363,6 +387,10 @@ int lttng_filter_interpret_bytecode(void *filter_data,
                /* logical */
                [ FILTER_OP_AND ] = &&LABEL_FILTER_OP_AND,
                [ FILTER_OP_OR ] = &&LABEL_FILTER_OP_OR,
+               [ FILTER_OP_AND_S64 ] = &&LABEL_FILTER_OP_AND_S64,
+               [ FILTER_OP_OR_S64 ] = &&LABEL_FILTER_OP_OR_S64,
+               [ FILTER_OP_AND_DOUBLE ] = &&LABEL_FILTER_OP_AND_DOUBLE,
+               [ FILTER_OP_OR_DOUBLE ] = &&LABEL_FILTER_OP_OR_DOUBLE,
 
                /* load */
                [ FILTER_OP_LOAD_FIELD_REF ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF,
@@ -625,12 +653,18 @@ int lttng_filter_interpret_bytecode(void *filter_data,
 
                /* logical */
                OP(FILTER_OP_AND):
+               OP(FILTER_OP_OR):
+                       ERR("unsupported non-specialized bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               OP(FILTER_OP_AND_S64):
                {
                        struct logical_op *insn = (struct logical_op *) pc;
 
                        /* If REG_R0 is 0, skip and evaluate to 0 */
-                       if ((reg[REG_R0].type == REG_S64 && reg[REG_R0].v == 0)
-                                       || unlikely(reg[REG_R0].type == REG_DOUBLE && reg[REG_R0].d == 0.0)) {
+                       if (unlikely(reg[REG_R0].v == 0)) {
                                dbg_printf("Jumping to bytecode offset %u\n",
                                        (unsigned int) insn->skip_offset);
                                next_pc = start_pc + insn->skip_offset;
@@ -639,14 +673,13 @@ int lttng_filter_interpret_bytecode(void *filter_data,
                        }
                        PO;
                }
-               OP(FILTER_OP_OR):
+               OP(FILTER_OP_OR_S64):
                {
                        struct logical_op *insn = (struct logical_op *) pc;
 
                        /* If REG_R0 is nonzero, skip and evaluate to 1 */
 
-                       if ((reg[REG_R0].type == REG_S64 && reg[REG_R0].v != 0)
-                                       || unlikely(reg[REG_R0].type == REG_DOUBLE && reg[REG_R0].d != 0.0)) {
+                       if (unlikely(reg[REG_R0].v != 0)) {
                                reg[REG_R0].v = 1;
                                dbg_printf("Jumping to bytecode offset %u\n",
                                        (unsigned int) insn->skip_offset);
@@ -657,6 +690,38 @@ int lttng_filter_interpret_bytecode(void *filter_data,
                        PO;
                }
 
+               OP(FILTER_OP_AND_DOUBLE):
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       /* If REG_R0 is 0, skip and evaluate to 0 */
+                       if ((reg[REG_R0].type == REG_DOUBLE && unlikely(reg[REG_R0].d == 0.0))
+                                       || (reg[REG_R0].type == REG_S64 && unlikely(reg[REG_R0].v == 0))) {
+                               dbg_printf("Jumping to bytecode offset %u\n",
+                                       (unsigned int) insn->skip_offset);
+                               next_pc = start_pc + insn->skip_offset;
+                       } else {
+                               next_pc += sizeof(struct logical_op);
+                       }
+                       PO;
+               }
+               OP(FILTER_OP_OR_DOUBLE):
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       /* If REG_R0 is nonzero, skip and evaluate to 1 (in double) */
+                       if ((reg[REG_R0].type == REG_DOUBLE && unlikely(reg[REG_R0].d != 0.0))
+                                       || (reg[REG_R0].type == REG_S64 && unlikely(reg[REG_R0].v != 0))) {
+                               reg[REG_R0].d = 1.0;
+                               dbg_printf("Jumping to bytecode offset %u\n",
+                                       (unsigned int) insn->skip_offset);
+                               next_pc = start_pc + insn->skip_offset;
+                       } else {
+                               next_pc += sizeof(struct logical_op);
+                       }
+                       PO;
+               }
+
                /* load */
                OP(FILTER_OP_LOAD_FIELD_REF_STRING):
                {
@@ -774,6 +839,11 @@ end:
        return retval;
 }
 
+#undef START_OP
+#undef OP
+#undef PO
+#undef END_OP
+
 static
 int bin_op_compare_check(struct vreg reg[NR_REG], const char *str)
 {
@@ -974,6 +1044,11 @@ int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode)
                                ret = -EINVAL;
                                goto end;
                        }
+                       if (reg[REG_R0].type != REG_DOUBLE && reg[REG_R1].type != REG_DOUBLE) {
+                               ERR("Double operator should have at least one double register\n");
+                               ret = -EINVAL;
+                               goto end;
+                       }
                        reg[REG_R0].type = REG_DOUBLE;
                        next_pc += sizeof(struct binary_op);
                        break;
@@ -1056,6 +1131,10 @@ int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode)
                /* logical */
                case FILTER_OP_AND:
                case FILTER_OP_OR:
+               case FILTER_OP_AND_S64:
+               case FILTER_OP_OR_S64:
+               case FILTER_OP_AND_DOUBLE:
+               case FILTER_OP_OR_DOUBLE:
                {
                        struct logical_op *insn = (struct logical_op *) pc;
 
@@ -1075,6 +1154,41 @@ int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode)
                                ret = -EINVAL;
                                goto end;
                        }
+                       if (insn->op == FILTER_OP_AND_S64
+                                       || insn->op == FILTER_OP_OR_S64) {
+                               if (reg[REG_R0].type != REG_S64
+                                               || reg[REG_R1].type != REG_S64) {
+                                       ret = -EINVAL;
+                                       goto end;
+                               }
+                       }
+                       if (insn->op == FILTER_OP_AND_DOUBLE
+                                       || insn->op == FILTER_OP_OR_DOUBLE) {
+                               if (reg[REG_R0].type != REG_DOUBLE
+                                               && reg[REG_R1].type != REG_DOUBLE) {
+                                       ERR("Double operator should have at least one double register\n");
+                                       ret = -EINVAL;
+                                       goto end;
+                               }
+                       }
+                       switch(reg[REG_R0].type) {
+                       default:
+                       case REG_STRING:
+                               ERR("unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               if (reg[REG_R1].type == REG_S64) {
+                                       reg[REG_R0].type = REG_S64;
+                               } else {
+                                       reg[REG_R0].type = REG_DOUBLE;
+                               }
+                               break;
+                       case REG_DOUBLE:
+                               reg[REG_R0].type = REG_DOUBLE;
+                               break;
+                       }
                        next_pc += sizeof(struct logical_op);
                        break;
                }
@@ -1450,6 +1564,7 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode)
                                insn->op = FILTER_OP_UNARY_PLUS_DOUBLE;
                                break;
                        }
+                       next_pc += sizeof(struct unary_op);
                        break;
                }
 
@@ -1470,6 +1585,7 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode)
                                insn->op = FILTER_OP_UNARY_MINUS_DOUBLE;
                                break;
                        }
+                       next_pc += sizeof(struct unary_op);
                        break;
                }
 
@@ -1490,6 +1606,7 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode)
                                insn->op = FILTER_OP_UNARY_NOT_DOUBLE;
                                break;
                        }
+                       next_pc += sizeof(struct unary_op);
                        break;
                }
 
@@ -1506,7 +1623,66 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode)
 
                /* logical */
                case FILTER_OP_AND:
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       switch(reg[REG_R0].type) {
+                       default:
+                       case REG_STRING:
+                               ERR("unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               if (reg[REG_R1].type == REG_S64) {
+                                       insn->op = FILTER_OP_AND_S64;
+                                       reg[REG_R0].type = REG_S64;
+                               } else {
+                                       insn->op = FILTER_OP_AND_DOUBLE;
+                                       reg[REG_R0].type = REG_DOUBLE;
+                               }
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_AND_DOUBLE;
+                               reg[REG_R0].type = REG_DOUBLE;
+                               break;
+                       }
+                       next_pc += sizeof(struct logical_op);
+                       break;
+               }
                case FILTER_OP_OR:
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       switch(reg[REG_R0].type) {
+                       default:
+                       case REG_STRING:
+                               ERR("unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               if (reg[REG_R1].type == REG_S64) {
+                                       insn->op = FILTER_OP_OR_S64;
+                                       reg[REG_R0].type = REG_S64;
+                               } else {
+                                       insn->op = FILTER_OP_OR_DOUBLE;
+                                       reg[REG_R0].type = REG_DOUBLE;
+                               }
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_OR_DOUBLE;
+                               reg[REG_R0].type = REG_DOUBLE;
+                               break;
+                       }
+                       next_pc += sizeof(struct logical_op);
+                       break;
+               }
+
+               case FILTER_OP_AND_S64:
+               case FILTER_OP_OR_S64:
+               case FILTER_OP_AND_DOUBLE:
+               case FILTER_OP_OR_DOUBLE:
                {
                        next_pc += sizeof(struct logical_op);
                        break;
This page took 0.026959 seconds and 4 git commands to generate.