Date: Tue, 26 Aug 2014 14:44:44 GMT From: dpl@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r273169 - soc2014/dpl/netmap-ipfwjit/sys/netpfil/ipfw Message-ID: <201408261444.s7QEiiXe080257@socsvn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: dpl Date: Tue Aug 26 14:44:44 2014 New Revision: 273169 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=273169 Log: Corrected most wrong code, added warnings. Modified: soc2014/dpl/netmap-ipfwjit/sys/netpfil/ipfw/jit.cc Modified: soc2014/dpl/netmap-ipfwjit/sys/netpfil/ipfw/jit.cc ============================================================================== --- soc2014/dpl/netmap-ipfwjit/sys/netpfil/ipfw/jit.cc Tue Aug 26 14:43:46 2014 (r273168) +++ soc2014/dpl/netmap-ipfwjit/sys/netpfil/ipfw/jit.cc Tue Aug 26 14:44:44 2014 (r273169) @@ -16,6 +16,7 @@ #include <llvm/IR/Module.h> #include <llvm/IR/Type.h> #include <llvm/IR/Value.h> +#include <llvm/IR/Verifier.h> #include <llvm/Support/MemoryBuffer.h> #include <llvm/Support/ErrorOr.h> #include <llvm/Support/TargetSelect.h> @@ -49,8 +50,8 @@ IRBuilder<> irb; // We'll store the BasicBlock objects for each rule here. - int rule_number; - std::vector<BasicBlock*> blocks; + int rulenumber = 1; + std::vector<BasicBlock *> rules; // Vars Types Type *int8Ty; @@ -62,7 +63,7 @@ // Basic blocks used BasicBlock *entry, *end; BasicBlock *pullup_failed; - BasicBlock *startrules; + BasicBlock *check_tag; BasicBlock *outer_for_prologue; BasicBlock *inner_for_prologue; BasicBlock *outer_for_epilogue; @@ -277,6 +278,8 @@ } // Allocate and initialize LLVM vars. + // Note: The type of the object returned by CreateStore + // is already a pointer to a given type. void allocaAndInit() { @@ -284,37 +287,37 @@ // Control flow variables. match = irb.CreateAlloca(int32Ty); l = irb.CreateAlloca(int32Ty); + done = irb.CreateAlloca(int32Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), done); + f_pos = irb.CreateAlloca(int32Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), f_pos); + retval = irb.CreateAlloca(int32Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), retval); - cmd = irb.CreateAlloca(ipfw_insnPtrTy); + + cmd = irb.CreateAlloca(ipfw_insnTy); tablearg = irb.CreateAlloca(int32Ty); cmdlen = irb.CreateAlloca(int32Ty); skip_or = irb.CreateAlloca(int32Ty); - f = irb.CreateAlloca(ip_fwPtrTy); + f = irb.CreateAlloca(ip_fwTy); // m = args->m (idx: 0) m = irb.CreateAlloca(mbufPtrTy); - irb.CreateStore(irb.CreateInBoundsGEP(irb.CreateLoad(args), ConstantInt::get(int32Ty, 0)), m); + irb.CreateStore(irb.CreateStructGEP(args, 0), m); // ip = (struct ip *)((m)->m_data) (idx: 2) ip = irb.CreateAlloca(ipPtrTy); - irb.CreateStore(irb.CreateBitCast(irb.CreateInBoundsGEP(irb.CreateLoad(args), ConstantInt::get(int32Ty, 2)), ipPtrTy), ip); + irb.CreateStore(irb.CreateBitCast(irb.CreateStructGEP(irb.CreateLoad(m), 2), ipPtrTy), ip); -#ifdef __FreeBSD__ - ucred_cache = irb.CreateAlloca(ucredPtrTy); // Init: NULL if type ucred. - irb.CreateStore(ConstantPointerNull::get(ucredPtrTy), ucred_cache); -#else - ucred_cache = irb.CreateAlloca(ucredTy); -#endif + ucred_cache = irb.CreateAlloca(ucredTy); // Init: NULL if type ucred. ucred_lookup = irb.CreateAlloca(int32Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), ucred_lookup); - oif = irb.CreateAlloca(ifnetPtrTy); // Init: args->oif + oif = irb.CreateAlloca(ifnetTy); // Init: args->oif + irb.CreateLoad(irb.CreateStructGEP(args, 1), oif); hlen = irb.CreateAlloca(int32Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), hlen); @@ -330,7 +333,7 @@ proto = irb.CreateAlloca(int8Ty); irb.CreateStore(ConstantInt::get(int8Ty, 0), proto); // args->f_id.proto = 0 (idx: 6, 5) - irb.CreateStore(ConstantInt::get(int8Ty, 0), irb.CreateInBoundsGEP(irb.CreateLoad(args), {ConstantInt::get(int32Ty, 6), ConstantInt::get(int32Ty, 5)} )); + irb.CreateStore(ConstantInt::get(int8Ty, 0), irb.CreateStructGEP(irb.CreateStructGEP(args, 6), 5)); src_port = irb.CreateAlloca(int16Ty); irb.CreateStore(ConstantInt::get(int16Ty, 0), src_port); @@ -339,11 +342,12 @@ //src_ip.s_addr = 0; src_ip = irb.CreateAlloca(in_addrTy); - irb.CreateStore(ConstantInt::get(int32Ty, 0), irb.CreateInBoundsGEP(src_ip, ConstantInt::get(int32Ty, 0))); + irb.CreateStore(ConstantInt::get(int32Ty, 0), irb.CreateStructGEP(src_ip, 0)); //dst_ip.s_addr = 0; dst_ip = irb.CreateAlloca(in_addrTy); - irb.CreateStore(ConstantInt::get(int32Ty, 0), irb.CreateInBoundsGEP(dst_ip, ConstantInt::get(int32Ty, 0))); + irb.CreateStore(ConstantInt::get(int32Ty, 0), irb.CreateStructGEP(dst_ip, 0)); + //iplen = 0; iplen = irb.CreateAlloca(int16Ty); irb.CreateStore(ConstantInt::get(int16Ty, 0), iplen); @@ -351,7 +355,7 @@ // m_pkthdr is the 6th element (idx: 5) // len is the 2nd element (idx: 1) pktlen = irb.CreateAlloca(int32Ty); - irb.CreateStore(ConstantInt::get(int32Ty, 0), irb.CreateInBoundsGEP(irb.CreateLoad(m), {ConstantInt::get(int32Ty, 5), ConstantInt::get(int32Ty, 1)} )); + irb.CreateStore(irb.CreateStructGEP(irb.CreateStructGEP(irb.CreateLoad(m), 5), 1), pktlen); etype = irb.CreateAlloca(int16Ty); irb.CreateStore(ConstantInt::get(int32Ty, 0), etype); @@ -374,6 +378,10 @@ irb.CreateStore(ConstantInt::get(int8Ty, 0), icmp6_type); ext_hd = irb.CreateAlloca(int16Ty); irb.CreateStore(ConstantInt::get(int16Ty, 0), ext_hd); + + // If it returns one, goto pullup_failed. + // Else, goto first rule. + irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateCall(inspect_pkt, {args, ip, m, src_ip, dst_ip, src_port, dst_port, etype, ext_hd, iplen, pktlen, is_ipv4, is_ipv6, hlen, proto, icmp6_type, ip6f_mf, offset, ulp}), ConstantInt::get(int32Ty, 1)), pullup_failed, check_tag); } void @@ -418,6 +426,8 @@ BasicBlock *jt = BasicBlock::Create(con, "jt", func); BasicBlock *jf = BasicBlock::Create(con, "jf", func); + irb.SetInsertPoint(check_tag); + // if (args->rule.slot) { // /* // * Packet has already been tagged as a result of a previous @@ -435,19 +445,19 @@ // } // if (args->rule.slot) - irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 4),ConstantInt::get(int32Ty, 0)}), ConstantInt::get(int32Ty, 0)), nottagged, tagged); + irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 4),ConstantInt::get(int32Ty, 0)}), ConstantInt::get(int32Ty, 0)), nottagged, tagged); // if (args->rule.chain_id == chain->id) irb.SetInsertPoint(tagged); - irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 3)}), irb.CreateInBoundsGEP(chain, ConstantInt::get(int32Ty, 12))), jt, jf); + irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 3)}), irb.CreateInBoundsGEP(chain, ConstantInt::get(int32Ty, 12))), jt, jf); // f_pos = args->rule.slot; irb.SetInsertPoint(jt); - irb.CreateStore(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 4),ConstantInt::get(int32Ty, 0)}), f_pos); + irb.CreateStore(irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 4),ConstantInt::get(int32Ty, 0)}), f_pos); irb.CreateBr(nottagged); // else fpos = ipfw_find_rule(chain, args->rule.rulenum, args->rule.rule_id) irb.SetInsertPoint(jf); - irb.CreateStore(irb.CreateCall3(ipfw_find_rule, chain, irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 1)}), irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 2)})), f_pos); + irb.CreateStore(irb.CreateCall3(ipfw_find_rule, chain, irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 1)}), irb.CreateInBoundsGEP(args, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 4), ConstantInt::get(int32Ty, 2)})), f_pos); // Branch to nottagged because it // only finishes the entry BasicBlock. @@ -456,7 +466,7 @@ // else f_pos = 0; // Since f_pos is initialized by default as 0, we only br. irb.SetInsertPoint(nottagged); - irb.CreateBr(startrules); + irb.CreateBr(rules[rulenumber]); } void @@ -480,11 +490,12 @@ // uint32_t tablearg = 0; irb.CreateStore(ConstantInt::get(int32Ty, 0), tablearg); - // f = chain->map[f_pos]; - irb.CreateStore(irb.CreateInBoundsGEP(irb.CreateLoad(chain), {ConstantInt::get(int32Ty, 5), f_pos}), f); + // f = chain->map[f_pos]; idxs: 5, f_pos + irb.CreateStore(irb.CreateInBoundsGEP(irb.CreateLoad(irb.CreateStructGEP(chain, 5)), f_pos), f); // if (V_set_disable & (1 << f->set) ) - irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(set_disable, irb.CreateShl(ConstantInt::get(int32Ty, 1), irb.CreateInBoundsGEP(f, ConstantInt::get(int32Ty, 5)))), ConstantInt::get(int32Ty, 0)), jt, jf); + irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(set_disable, irb.CreateShl(ConstantInt::get(int32Ty, 1), irb.CreateStructGEP(f, 5))), ConstantInt::get(int32Ty, 0)), jt, jf); + irb.SetInsertPoint(jt); // continue; @@ -521,21 +532,21 @@ // l = f->cmd_len; // XXX - ask David about types. int l; uint16_t cmd_len; - irb.CreateStore(irb.CreateInBoundsGEP(f, ConstantInt::get(int32Ty, 3)), l); + irb.CreateStore(irb.CreateInBoundsGEP(f, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 3)}), l); // cmd = f->cmd; - irb.CreateStore(irb.CreateInBoundsGEP(f, ConstantInt::get(int32Ty, 11)), cmd); + irb.CreateStore(irb.CreateInBoundsGEP(f, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 11)}), cmd); // int match; // match is already allocated. // cmdlen = ((cmd)->len & F_LEN_MASK); - irb.CreateStore(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, ConstantInt::get(int32Ty, 1)), ConstantInt::get(int8Ty, F_LEN_MASK)), cmdlen); + irb.CreateStore(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 1)}), ConstantInt::get(int8Ty, F_LEN_MASK)), cmdlen); // if (skip_or) irb.CreateCondBr(irb.CreateICmpNE(skip_or, ConstantInt::get(int32Ty, 0)), firstt, firstf); irb.SetInsertPoint(firstt); // if ((cmd->len & F_OR) == 0) - irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, ConstantInt::get(int32Ty, 1)), ConstantInt::get(int8Ty, F_OR)), ConstantInt::get(int8Ty, 0)), secondt, secondf); + irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 1)}), ConstantInt::get(int8Ty, F_OR)), ConstantInt::get(int8Ty, 0)), secondt, secondf); irb.SetInsertPoint(secondt); // skip_or = 0; @@ -557,6 +568,8 @@ void emit_inner_for_epilogue() { + BasicBlock *matchnz = BasicBlock::Create(con, "jt", func); + BasicBlock *matchz = BasicBlock::Create(con, "jt", func); BasicBlock *jt = BasicBlock::Create(con, "jt", func); BasicBlock *jf = BasicBlock::Create(con, "jf", func); BasicBlock *sec_cond = BasicBlock::Create(con, "sec_cond", func); @@ -584,11 +597,19 @@ irb.SetInsertPoint(inner_for_epilogue); // if (cmd->len & F_NOT) - irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, ConstantInt::get(int32Ty, 1)), ConstantInt::get(int8Ty, F_NOT)), ConstantInt::get(int32Ty, 0)), jt, sec_cond); + irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 1)}), ConstantInt::get(int8Ty, F_NOT)), ConstantInt::get(int32Ty, 0)), jt, sec_cond); irb.SetInsertPoint(jt); // match = !match; - irb.CreateStore(irb.CreateNot(match), match); + // match = ((match)?0:1); + irb.CreateCondBr(irb.CreateICmpNE(match, ConstantInt::get(int32Ty, 0)),matchnz, matchz); + + irb.SetInsertPoint(matchnz); + irb.CreateStore(ConstantInt::get(int32Ty, 0), match); + irb.CreateBr(sec_cond); + + irb.SetInsertPoint(matchz); + irb.CreateStore(ConstantInt::get(int32Ty, 1), match); irb.CreateBr(sec_cond); irb.SetInsertPoint(sec_cond); @@ -597,7 +618,7 @@ irb.SetInsertPoint(matchnotzero); // if (cmd->len & F_OR) - irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, ConstantInt::get(int32Ty, 1)), ConstantInt::get(int8Ty, F_OR)), ConstantInt::get(int32Ty, 0)), is_or, outer_for_epilogue); + irb.CreateCondBr(irb.CreateICmpNE(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 1)}), ConstantInt::get(int8Ty, F_OR)), ConstantInt::get(int32Ty, 0)), is_or, outer_for_epilogue); irb.SetInsertPoint(is_or); // skip_or = 1; @@ -607,7 +628,7 @@ irb.SetInsertPoint(matchzero); // if (!(cmd->len & F_OR)) /* not an OR block, */ // break; - irb.CreateCondBr(irb.CreateICmpNE(irb.CreateNot(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, ConstantInt::get(int32Ty, 1)), ConstantInt::get(int8Ty, F_OR))), ConstantInt::get(int32Ty, 0)), next /* break */, outer_for_epilogue); + irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateAnd(irb.CreateInBoundsGEP(cmd, {ConstantInt::get(int32Ty, 0), ConstantInt::get(int32Ty, 1)}), ConstantInt::get(int8Ty, F_OR)), ConstantInt::get(int32Ty, 0)), next /* break */, outer_for_epilogue); } // This code gets executed at the end of inner loop. @@ -615,19 +636,20 @@ void emit_outer_for_epilogue() { + irb.SetInsertPoint(outer_for_epilogue); + // f_pos++, increment of the for loop. irb.CreateStore(irb.CreateAdd(f_pos, ConstantInt::get(int32Ty, 1)), f_pos); // if (done) // break; irb.CreateCondBr(irb.CreateICmpNE(done, ConstantInt::get(int32Ty, 0)), end, next); - } void emit_end() { - Value *rule, *timestamp, *str; + Value *rule, *time_uptime, *str; BasicBlock *jt = BasicBlock::Create(con, "jt", func); BasicBlock *jf = BasicBlock::Create(con, "jf", func); @@ -653,7 +675,7 @@ irb.SetInsertPoint(end); // We need to get the timestamp variable. - timestamp = mod->getGlobalVariable("timestamp"); + time_uptime = mod->getGlobalVariable("time_uptime"); str = irb.CreateGlobalString("ipfw: ouch!, skip past end of rules, denying packet\n"); // if (done) @@ -662,18 +684,24 @@ irb.SetInsertPoint(jt); // struct ip_fw *rule = chain->map[f_pos]; rule = irb.CreateAlloca(ip_fwPtrTy); - irb.CreateStore(irb.CreateInBoundsGEP(chain, {ConstantInt::get(int32Ty, 5), f_pos}), rule); + irb.CreateStore(irb.CreateInBoundsGEP(irb.CreateLoad(irb.CreateStructGEP(chain, 5)), f_pos), rule); // uint64_t pcnt; // (rule)->pcnt++; - irb.CreateStore(irb.CreateInBoundsGEP(rule, ConstantInt::get(int32Ty,8)), irb.CreateAdd(irb.CreateInBoundsGEP(rule, ConstantInt::get(int32Ty, 8)), ConstantInt::get(int64Ty, 1))); + Value *pcnt = irb.CreateStructGEP(irb.CreateLoad(rule), 8); + irb.CreateStore(irb.CreateAdd(pcnt, ConstantInt::get(pcnt->getType(), 1)), pcnt); // uint64_t bnct; // (rule)->bcnt += pktlen; - irb.CreateStore(irb.CreateInBoundsGEP(rule, ConstantInt::get(int32Ty, 9)), irb.CreateAdd(irb.CreateInBoundsGEP(rule, ConstantInt::get(int32Ty, 9)), pktlen)); + // XXX pktlen->getType(): int32Ty + Value *bcnt = irb.CreateStructGEP(irb.CreateLoad(rule), 9); + irb.CreateStore(irb.CreateAdd(bcnt, pktlen), bcnt); // (rule)->timestamp = time_uptime; // uint32_t timestamp; - irb.CreateStore(irb.CreateInBoundsGEP(rule, ConstantInt::get(int32Ty, 10)), timestamp); + // XXX timestamp->getType(): int32Ty + // XXX time_uptime: int64Ty + Value *timestamp = irb.CreateStructGEP(irb.CreateLoad(rule), 10); + irb.CreateStore(time_uptime, timestamp); irb.SetInsertPoint(jf); // retval = IP_FW_DENY; @@ -703,7 +731,7 @@ } public: - ipfwJIT(): irb(con) + ipfwJIT(int rulesnumber): irb(con) { // Create the module and load the code. mod = loadbc("ip_fw_rules.bc"); @@ -711,17 +739,21 @@ func = mod->getFunction("ipfw_chk_jit"); func->setLinkage(GlobalValue::ExternalLinkage); - // Create statics BasicBlocks. + // Create static BasicBlocks. // The entry basic block contains all the initialization // and allocation of resources, and a basic check done // before start emmiting the rules code. entry = BasicBlock::Create(con, "entry", func); + check_tag = BasicBlock::Create(con, "check_tag", func); + end = BasicBlock::Create(con, "end", func); + + // Initialize the vector. + rules = std::vector<BasicBlock *>(rulesnumber + 1); + rules[rulenumber] = BasicBlock::Create(con, "rule", func); + start_rule(); + // This is equivalent to the pullup_failed tag. pullup_failed = BasicBlock::Create(con, "pullup_failed", func); - // This will be the first BasicBlock to store our emmited code. - startrules = BasicBlock::Create(con, "startrules", func); - // This will be executed at the end of ipfw_chk_jit(). - end = BasicBlock::Create(con, "end", func); //Snippets of code to be executed when iterating through the rules. outer_for_prologue = BasicBlock::Create(con, "outer_for_prologue", func); @@ -731,8 +763,9 @@ // Get struct types, and store vars setEnv(); - allocaAndInit(); + // Start compilation + allocaAndInit(); emit_check_tag(); emit_pullup_failed(); @@ -743,6 +776,11 @@ emit_outer_for_epilogue(); emit_end(); + verifyFunction(*func); + + + verifyModule(*mod); + mod->dump(); } ~ipfwJIT() { @@ -753,6 +791,7 @@ void optimize() { + // We don't need it anymore. Function *vf = mod->getFunction("voidfunction"); vf->eraseFromParent(); } @@ -764,18 +803,26 @@ return ((funcptr)NULL); } - // Call the function that fills in some vars. void - emit_lookpkt_call() + start_rule() { - // If it returns one, goto pullup_failed. - // Else, goto starrules. - irb.CreateCondBr(irb.CreateICmpEQ(irb.CreateCall(inspect_pkt, {args, ip, m, src_ip, dst_ip, src_port, dst_port, etype, ext_hd, iplen, pktlen, is_ipv4, is_ipv6, hlen, proto, icmp6_type, ip6f_mf, offset, ulp}), ConstantInt::get(int32Ty, 1)), pullup_failed, startrules); + // This will be the first BasicBlock to store our emmited code. + rules[rulenumber] = BasicBlock::Create(con, "rule", func); + next = rules[rulenumber+1] = BasicBlock::Create(con, "rule", func); } void + end_rule() + { + // We're on the next rule now. + rulenumber++; + } + + // Also initialized the rules vector. + void emit_outer_for_prologue_call() { + irb.SetInsertPoint(rules[rulenumber]); irb.CreateBr(outer_for_prologue); } @@ -843,10 +890,7 @@ InitializeNativeTarget(); LLVMLinkInJIT(); - ipfwJIT compiler; - - // Fill up needed local variables. - compiler.emit_lookpkt_call(); + ipfwJIT compiler(chain->n_rules); // Iterate through the rules. int pktlen = args->m->m_pkthdr.len; @@ -860,6 +904,8 @@ f = chain->map[f_pos]; + // Rule start. + compiler.start_rule(); compiler.emit_outer_for_prologue_call(); // For each different command. @@ -1251,7 +1297,9 @@ } /* end of switch() on opcodes */ compiler.emit_inner_for_prologue_call(); } /* end of inner loop, scan opcodes */ - compiler.emit_outer_for_prologue_call(); + // Rule ends. + compiler.emit_outer_for_epilogue_call(); + compiler.end_rule(); } /* end of outer for, scan rules */ compiler.emit_end_call();
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201408261444.s7QEiiXe080257>