offset_to_string(struct bvm_asm *a, int c);
static struct bvm_asmnode *
insn_after_label(struct bvm_asm *a, int label);
-static void
-opt_thread_jumps(struct bvm_asm *a);
-static void
+static void optimize_bvm_ast(struct boolexp_node *);
+ static void
optimize_bvm_asm(struct bvm_asm *a);
static boolexp
emit_bytecode(struct bvm_asm *a, int derefs);
return NULL;
}
-/** Avoid jumps that lead straight to another jump. If the second jump
- * is on the same condition as the first one, jump instead to its
- * destination. If it's the opposite condition, jump instead to the
- * first instruction after the second jump to avoid the useless
- * conditional check.
- * \param a the assembler list to thread. */
+/** Do some trivial optimizations at the syntax tree level. Some of
+ * these catch things that no normal person would do with a lock, but
+ * might be created artificially at some point in the future -- for
+ * example, if we ever go through and replace reference to a deleted
+ * object with #false in locks.
+ *
+ * Current optimizations:
+ *
+ * Turn =#123|+#123 into the equivalent #123 (Won't work with
+ * =#123|+#123|foo); doing so is probably overkill.)
+ * Turn !!foo into foo
+ * Turn !#TRUE into #FALSE and vis versa
+ *
+ * Possible future additions:
+ * Change foo&#FALSE&bar into #FALSE
+ * Change foo|#TRUE|bar into #TRUE
+ *
+ * \param ast the syntax tree to transform
+ */
+static void
+optimize_bvm_ast(struct boolexp_node *ast)
+{
+ struct boolexp_node *temp;
+
+ if (!ast)
+ return;
+ switch (ast->type) {
+ case BOOLEXP_OR:
+ if (((ast->data.sub.a->type == BOOLEXP_IS && ast->data.sub.b->type == BOOLEXP_CARRY)
+ || (ast->data.sub.a->type == BOOLEXP_CARRY && ast->data.sub.b->type == BOOLEXP_IS))
+ && (ast->data.sub.a->thing == ast->data.sub.b->thing)) {
+ /* Turn =#123|+#123 into #123 */
+
+ dbref thing = ast->data.sub.a->thing;
+
+ free_bool(ast->data.sub.a);
+ free_bool(ast->data.sub.b);
+ ast->type = BOOLEXP_CONST;
+ ast->thing = thing;
+ ast->data.sub.a = ast->data.sub.b = NULL;
+ } else {
+ optimize_bvm_ast(ast->data.sub.a);
+ optimize_bvm_ast(ast->data.sub.b);
+ }
+ break;
+ case BOOLEXP_AND:
+ optimize_bvm_ast(ast->data.sub.a);
+ optimize_bvm_ast(ast->data.sub.b);
+ break;
+ case BOOLEXP_NOT:
+ temp = ast->data.n;
+ if (temp->type == BOOLEXP_NOT) {
+ /* Turn !!foo into foo */
+ struct boolexp_node *n = temp->data.n;
+ free_bool(temp);
+ ast->type = n->type;
+ ast->thing = n->thing;
+ ast->data = n->data;
+ free_bool(n);
+ optimize_bvm_ast(ast);
+ } else if (temp->type == BOOLEXP_BOOL) {
+ /* Turn !#true into #false */
+ ast->type = BOOLEXP_BOOL;
+ ast->thing = !temp->thing;
+ ast->data.n = NULL;
+ free_bool(temp);
+ } else
+ optimize_bvm_ast(ast->data.n);
+ break;
+ default:
+ (void)0; /* Nothing to do. */
+ }
+}
+
+/** Do some trivial optimizations of boolexp vm assembly.
+ *
+ *
+ * Current optimizations: Thread jumping
+ *
+ * Possible future additions:
+ * Just-in-time compiling of locks to machine code? Raevnos did this once as a proof of concept.
+ *
+ * \param a the assembler list to transform.
+ */
static void
-opt_thread_jumps(struct bvm_asm *a)
+optimize_bvm_asm(struct bvm_asm *a)
{
struct bvm_asmnode *n, *target;
+
+ if (!a)
+ return;
for (n = a->head; n;) {
+
+ /* Avoid jumps that lead straight to another jump. If the second
+ * jump is on the same condition as the first one, jump instead to its
+ * destination. If it's the opposite condition, jump instead to the
+ * first instruction after the second jump to avoid the useless
+ * conditional check.
+ */
if (n->op == OP_JMPT || n->op == OP_JMPF) {
target = insn_after_label(a, n->arg);
if (target && (target->op == OP_JMPT || target->op == OP_JMPF)) {
struct bvm_asmnode *newlbl;
newlbl = slab_malloc(bvm_asmnode_slab, NULL);
if (!newlbl)
- mush_panic(T
- ("Unable to allocate memory for boolexp asm node!"));
+ mush_panic(T("Unable to allocate memory for boolexp asm node!"));
newlbl->op = OP_LABEL;
n->arg = newlbl->arg = gen_label_id(a);
if (target->next)
}
}
-/** Do some trivial optimizations.
- * \param a the assembler list to transform.
- */
-static void
-optimize_bvm_asm(struct bvm_asm *a)
-{
- if (!a)
- return;
- opt_thread_jumps(a);
-}
-
/** Turn assembly into bytecode.
* \param a the assembly list to emit.
* \param the compiled bytecode.
ast = parse_boolexp_E();
if (!ast)
return TRUE_BOOLEXP;
+ optimize_bvm_ast(ast);
bvasm = generate_bvm_asm(ast);
if (!bvasm) {
free_boolexp_node(ast);
{"LIT", fun_lit, 1, -1, FN_LITERAL},
{"LJUST", fun_ljust, 2, 3, FN_REG},
{"LLOCKFLAGS", fun_lockflags, 0, 1, FN_REG},
- {"LLOCKS", fun_locks, 1, 1, FN_REG},
+ {"LLOCKS", fun_locks, 0, 1, FN_REG},
{"LMATH", fun_lmath, 2, 3, FN_REG},
{"LNUM", fun_lnum, 1, 4, FN_REG},
{"LOC", fun_loc, 1, 1, FN_REG},