summaryrefslogtreecommitdiff
path: root/src/libfsm/asm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libfsm/asm.cc')
-rw-r--r--src/libfsm/asm.cc2046
1 files changed, 2046 insertions, 0 deletions
diff --git a/src/libfsm/asm.cc b/src/libfsm/asm.cc
new file mode 100644
index 00000000..ecfe1c0f
--- /dev/null
+++ b/src/libfsm/asm.cc
@@ -0,0 +1,2046 @@
+/*
+ * Copyright 2014-2018 Adrian Thurston <thurston@colm.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ragel.h"
+#include "asm.h"
+#include "redfsm.h"
+#include "gendata.h"
+#include "bstmap.h"
+#include "ragel.h"
+#include "redfsm.h"
+#include "bstmap.h"
+#include "gendata.h"
+#include "parsedata.h"
+#include <sstream>
+
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::endl;
+using std::istream;
+using std::ifstream;
+using std::ostream;
+using std::ios;
+using std::cin;
+using std::endl;
+
+extern int numSplitPartitions;
+bool printStatistics = false;
+
+/* Enables transition logging in the form that score-based state sorting can
+ * processes. This bit of code is intended to increase locality and reduce
+ * cache misses. Gains are minimal, 1-2%. */
+// #define LOG_TRANS 1
+
+void asmLineDirective( ostream &out, const char *fileName, int line )
+{
+ /* Write the preprocessor line info for to the input file. */
+ out << "#line " << line << " \"";
+ for ( const char *pc = fileName; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ out << "\\\\";
+ else
+ out << *pc;
+ }
+ out << '"';
+
+ out << '\n';
+}
+
+/* Init code gen with in parameters. */
+AsmCodeGen::AsmCodeGen( const CodeGenArgs &args )
+:
+ CodeGenData( args ),
+ nextLmSwitchLabel( 1 ),
+ stackCS( false )
+{
+}
+
+void AsmCodeGen::genAnalysis()
+{
+ /* For directly executable machines there is no required state
+ * ordering. Choose a depth-first ordering to increase the
+ * potential for fall-throughs. */
+ redFsm->depthFirstOrdering();
+
+ /* Choose default transitions and make the flat transitions by character class. */
+ redFsm->chooseDefaultSpan();
+ redFsm->makeFlatClass();
+
+ /* If any errors have occured in the input file then don't write anything. */
+ if ( red->id->errorCount > 0 )
+ return;
+
+ redFsm->setInTrans();
+
+ /* Anlayze Machine will find the final action reference counts, among other
+ * things. We will use these in reporting the usage of fsm directives in
+ * action code. */
+ red->analyzeMachine();
+}
+
+/* Write out the fsm name. */
+string AsmCodeGen::FSM_NAME()
+{
+ return fsmName;
+}
+
+/* Emit the offset of the start state as a decimal integer. */
+string AsmCodeGen::START_STATE_ID()
+{
+ ostringstream ret;
+ ret << redFsm->startState->id;
+ return ret.str();
+};
+
+string AsmCodeGen::ACCESS()
+{
+ ostringstream ret;
+ if ( red->accessExpr != 0 )
+ INLINE_LIST( ret, red->accessExpr, 0, false, false );
+ return ret.str();
+}
+
+
+string AsmCodeGen::P()
+{
+ ostringstream ret;
+ if ( red->pExpr == 0 )
+ ret << "%r12";
+ else {
+ INLINE_LIST( ret, red->pExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::PE()
+{
+ ostringstream ret;
+ if ( red->peExpr == 0 )
+ ret << "%r13";
+ else {
+ INLINE_LIST( ret, red->peExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::vCS()
+{
+ ostringstream ret;
+ if ( red->csExpr == 0 ) {
+ if ( stackCS )
+ ret << "-48(%rbp)";
+ else
+ ret << "%r11";
+ }
+ else {
+ INLINE_LIST( ret, red->csExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::TOP()
+{
+ ostringstream ret;
+ if ( red->topExpr == 0 )
+ ret << "-64(%rbp)";
+ else {
+ ret << "(";
+ INLINE_LIST( ret, red->topExpr, 0, false, false );
+ ret << ")";
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::NFA_STACK()
+{
+ return string( "-80(%rbp)" );
+}
+
+string AsmCodeGen::NFA_TOP()
+{
+ return string( "-88(%rbp)" );
+}
+
+string AsmCodeGen::NFA_SZ()
+{
+ return string( "-96(%rbp)" );
+}
+
+string AsmCodeGen::STACK()
+{
+ ostringstream ret;
+ if ( red->stackExpr == 0 )
+ ret << "-56(%rbp)";
+ else {
+ ret << "(";
+ INLINE_LIST( ret, red->stackExpr, 0, false, false );
+ ret << ")";
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::vEOF()
+{
+ ostringstream ret;
+ if ( red->eofExpr == 0 )
+ ret << "-8(%rbp)";
+ else {
+ INLINE_LIST( ret, red->eofExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::TOKSTART()
+{
+ ostringstream ret;
+ if ( red->tokstartExpr == 0 )
+ ret << "-16(%rbp)";
+ else {
+ INLINE_LIST( ret, red->tokstartExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::TOKEND()
+{
+ ostringstream ret;
+ if ( red->tokendExpr == 0 )
+ ret << "-24(%rbp)";
+ else {
+ INLINE_LIST( ret, red->tokendExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::ACT()
+{
+ ostringstream ret;
+ if ( red->actExpr == 0 )
+ ret << "-32(%rbp)";
+ else {
+ INLINE_LIST( ret, red->actExpr, 0, false, false );
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::NBREAK()
+{
+ return string("-33(%rbp)");
+}
+
+string AsmCodeGen::GET_KEY()
+{
+ ostringstream ret;
+ if ( red->getKeyExpr != 0 ) {
+ /* Emit the user supplied method of retrieving the key. */
+ ret << "(";
+ INLINE_LIST( ret, red->getKeyExpr, 0, false, false );
+ ret << ")";
+ }
+ else {
+ /* Expression for retrieving the key, use simple dereference. */
+ ret << "(" << P() << ")";
+ }
+ return ret.str();
+}
+
+string AsmCodeGen::COND_KEY( CondKey key )
+{
+ ostringstream ret;
+ ret << "$" << key.getVal();
+ return ret.str();
+}
+
+
+/* Write out a key from the fsm code gen. Depends on wether or not the key is
+ * signed. */
+string AsmCodeGen::KEY( Key key )
+{
+ ostringstream ret;
+ ret << "$" << key.getVal();
+ return ret.str();
+}
+
+bool AsmCodeGen::isAlphTypeSigned()
+{
+ return keyOps->isSigned;
+}
+
+void AsmCodeGen::EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish )
+{
+ /* The parser gives fexec two children. The double brackets are for D
+ * code. If the inline list is a single word it will get interpreted as a
+ * C-style cast by the D compiler. */
+
+ ret <<
+ " subq $1, ";
+ INLINE_LIST( ret, item->children, targState, inFinish, false );
+ ret <<
+ "\n"
+ " movq ";
+ INLINE_LIST( ret, item->children, targState, inFinish, false );
+ ret << ", " << P() << "\n";
+}
+
+void AsmCodeGen::LM_SWITCH( ostream &ret, GenInlineItem *item,
+ int targState, int inFinish, bool csForced )
+{
+ long done = nextLmSwitchLabel++;
+
+ ret <<
+ " movq " << ACT() << ", %rax\n";
+
+ for ( GenInlineList::Iter lma = *item->children; lma.lte(); lma++ ) {
+ long l = nextLmSwitchLabel++;
+
+ /* Write the case label, the action and the case break. */
+ if ( lma->lmId < 0 ) {
+ }
+ else {
+ ret <<
+ " cmpq $" << lma->lmId << ", %rax\n"
+ " jne " << LABEL( "lm_switch_next", l ) << "\n";
+ }
+
+ INLINE_LIST( ret, lma->children, targState, inFinish, csForced );
+
+ ret <<
+ " jmp " << LABEL( "lm_done", done ) << "\n"
+ "" << LABEL( "lm_switch_next", l ) << ":\n";
+ }
+
+ ret <<
+ "" << LABEL( "lm_done", done ) << ":\n";
+}
+
+void AsmCodeGen::SET_ACT( ostream &ret, GenInlineItem *item )
+{
+ ret <<
+ " movq $" << item->lmId << ", " << ACT() << "\n";
+}
+
+void AsmCodeGen::SET_TOKEND( ostream &ret, GenInlineItem *item )
+{
+ /* Sets tokend, there may be an offset. */
+ ret <<
+ " movq " << P() << ", %rax\n";
+
+ if ( item->offset != 0 ) {
+ out <<
+ " addq $" << item->offset << ", %rax\n";
+ }
+
+ out <<
+ " movq %rax, " << TOKEND() << "\n";
+}
+
+void AsmCodeGen::GET_TOKEND( ostream &ret, GenInlineItem *item )
+{
+ ret <<
+ " movq " << TOKEND() << ", " << "%rax\n";
+}
+
+void AsmCodeGen::INIT_TOKSTART( ostream &ret, GenInlineItem *item )
+{
+ ret <<
+ " movq $0, " << TOKSTART() << "\n";
+}
+
+void AsmCodeGen::INIT_ACT( ostream &ret, GenInlineItem *item )
+{
+ ret <<
+ " movq $0, " << ACT() << "\n";
+}
+
+void AsmCodeGen::SET_TOKSTART( ostream &ret, GenInlineItem *item )
+{
+ ret <<
+ " movq " << P() << ", " << TOKSTART() << "\n";
+}
+
+void AsmCodeGen::HOST_STMT( ostream &ret, GenInlineItem *item,
+ int targState, bool inFinish, bool csForced )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, item->children, targState, inFinish, csForced );
+ }
+}
+
+void AsmCodeGen::HOST_EXPR( ostream &ret, GenInlineItem *item,
+ int targState, bool inFinish, bool csForced )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, item->children, targState, inFinish, csForced );
+ }
+}
+
+void AsmCodeGen::HOST_TEXT( ostream &ret, GenInlineItem *item,
+ int targState, bool inFinish, bool csForced )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, item->children, targState, inFinish, csForced );
+ }
+}
+
+void AsmCodeGen::GEN_STMT( ostream &ret, GenInlineItem *item,
+ int targState, bool inFinish, bool csForced )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, item->children, targState, inFinish, csForced );
+ }
+}
+
+void AsmCodeGen::GEN_EXPR( ostream &ret, GenInlineItem *item,
+ int targState, bool inFinish, bool csForced )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, item->children, targState, inFinish, csForced );
+ }
+}
+
+void AsmCodeGen::LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish )
+{
+ /* The parser gives fexec two children. The double brackets are for D code.
+ * If the inline list is a single word it will get interpreted as a C-style
+ * cast by the D compiler. This should be in the D code generator. */
+ INLINE_LIST( ret, item->children, targState, inFinish, false );
+
+ ret <<
+ " movq %rax, " << P() << "\n"
+ " subq $1, " << P() << "\n";
+}
+
+void AsmCodeGen::NBREAK( ostream &ret, int targState, bool csForced )
+{
+ outLabelUsed = true;
+ ret <<
+ " addq $1, " << P() << "\n";
+
+ if ( !csForced ) {
+ ret <<
+ " movq $" << targState << ", " << vCS() << "\n";
+ }
+
+ ret <<
+ " movb $1, " << NBREAK() << "\n"
+ " jmp " << LABEL( "pop" ) << "\n";
+}
+
+/* Write out an inline tree structure. Walks the list and possibly calls out
+ * to virtual functions than handle language specific items in the tree. */
+void AsmCodeGen::INLINE_LIST( ostream &ret, GenInlineList *inlineList,
+ int targState, bool inFinish, bool csForced )
+{
+ for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case GenInlineItem::Text:
+ ret << item->data;
+ break;
+ case GenInlineItem::Goto:
+ GOTO( ret, item->targState->id, inFinish );
+ break;
+ case GenInlineItem::Call:
+ CALL( ret, item->targState->id, targState, inFinish );
+ break;
+ case GenInlineItem::Next:
+ NEXT( ret, item->targState->id, inFinish );
+ break;
+ case GenInlineItem::Ret:
+ RET( ret, inFinish );
+ break;
+ case GenInlineItem::PChar:
+ ret << P();
+ break;
+ case GenInlineItem::Char:
+ ret << GET_KEY();
+ break;
+ case GenInlineItem::Hold:
+ ret <<
+ " subq $1, " << P() << "\n";
+ break;
+ case GenInlineItem::Exec:
+ EXEC( ret, item, targState, inFinish );
+ break;
+ case GenInlineItem::Curs:
+ CURS( ret, inFinish );
+ break;
+ case GenInlineItem::Targs:
+ TARGS( ret, inFinish, targState );
+ break;
+ case GenInlineItem::Entry:
+ ret << item->targState->id;
+ break;
+ case GenInlineItem::GotoExpr:
+ GOTO_EXPR( ret, item, inFinish );
+ break;
+ case GenInlineItem::CallExpr:
+ CALL_EXPR( ret, item, targState, inFinish );
+ break;
+ case GenInlineItem::NextExpr:
+ NEXT_EXPR( ret, item, inFinish );
+ break;
+ case GenInlineItem::LmSwitch:
+ LM_SWITCH( ret, item, targState, inFinish, csForced );
+ break;
+ case GenInlineItem::LmSetActId:
+ SET_ACT( ret, item );
+ break;
+ case GenInlineItem::LmSetTokEnd:
+ SET_TOKEND( ret, item );
+ break;
+ case GenInlineItem::LmGetTokEnd:
+ GET_TOKEND( ret, item );
+ break;
+ case GenInlineItem::LmInitTokStart:
+ INIT_TOKSTART( ret, item );
+ break;
+ case GenInlineItem::LmInitAct:
+ INIT_ACT( ret, item );
+ break;
+ case GenInlineItem::LmSetTokStart:
+ SET_TOKSTART( ret, item );
+ break;
+ case GenInlineItem::Break:
+ BREAK( ret, targState, csForced );
+ break;
+ /* Stubbed. */
+ case GenInlineItem::Ncall:
+ NCALL( ret, item->targState->id, targState, inFinish );
+ break;
+ case GenInlineItem::NcallExpr:
+ NCALL_EXPR( ret, item, targState, inFinish );
+ break;
+ case GenInlineItem::Nret:
+ NRET( ret, inFinish );
+ break;
+ case GenInlineItem::Nbreak:
+ NBREAK( ret, targState, csForced );
+ break;
+ case GenInlineItem::LmCase:
+ break;
+
+ case GenInlineItem::LmExec:
+ LM_EXEC( ret, item, targState, inFinish );
+ break;
+
+ case GenInlineItem::LmHold:
+ ret <<
+ " subq $1, " << P() << "\n";
+ break;
+ case GenInlineItem::NfaClear:
+ ret <<
+ " movq $0, " << NFA_TOP() << "\n";
+ break;
+
+ case GenInlineItem::HostStmt:
+ HOST_STMT( ret, item, targState, inFinish, csForced );
+ break;
+ case GenInlineItem::HostExpr:
+ HOST_EXPR( ret, item, targState, inFinish, csForced );
+ break;
+ case GenInlineItem::HostText:
+ HOST_TEXT( ret, item, targState, inFinish, csForced );
+ break;
+ case GenInlineItem::GenStmt:
+ GEN_STMT( ret, item, targState, inFinish, csForced );
+ break;
+ case GenInlineItem::GenExpr:
+ GEN_EXPR( ret, item, targState, inFinish, csForced );
+ break;
+ /* Handled at the top level. */
+ case GenInlineItem::NfaWrapAction:
+ case GenInlineItem::NfaWrapConds:
+ break;
+ }
+ }
+}
+/* Write out paths in line directives. Escapes any special characters. */
+string AsmCodeGen::LDIR_PATH( char *path )
+{
+ ostringstream ret;
+ for ( char *pc = path; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ ret << "\\\\";
+ else
+ ret << *pc;
+ }
+ return ret.str();
+}
+
+void AsmCodeGen::ACTION( ostream &ret, GenAction *action, int targState,
+ bool inFinish, bool csForced )
+{
+ /* Write the preprocessor line info for going into the source file. */
+ asmLineDirective( ret, action->loc.fileName, action->loc.line );
+
+ /* Write the block and close it off. */
+ INLINE_LIST( ret, action->inlineList, targState, inFinish, csForced );
+}
+
+void AsmCodeGen::CONDITION( ostream &ret, GenAction *condition )
+{
+ ret << "\n";
+ asmLineDirective( ret, condition->loc.fileName, condition->loc.line );
+ INLINE_LIST( ret, condition->inlineList, 0, false, false );
+}
+
+bool singleItem( GenAction *action, GenInlineItem::Type type )
+{
+ return action->inlineList->length() == 1 &&
+ action->inlineList->head->type == type;
+}
+
+void AsmCodeGen::NFA_CONDITION( ostream &ret, GenAction *condition, bool last )
+{
+ if ( singleItem( condition, GenInlineItem::NfaWrapAction ) )
+ {
+ GenAction *action = condition->inlineList->head->wrappedAction;
+ ACTION( out, action, 0, false, false );
+ }
+ else if ( singleItem( condition, GenInlineItem::NfaWrapConds ) )
+ {
+ GenCondSpace *condSpace = condition->inlineList->head->condSpace;
+ const CondKeySet &condKeySet = condition->inlineList->head->condKeySet;
+
+ out << " movq $0, %r9\n";
+ for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
+ out <<
+ " pushq %r9\n";
+
+ CONDITION( out, *csi );
+ out <<
+ "\n"
+ " test %eax, %eax\n"
+ " setne %cl\n"
+ " movsbq %cl, %rcx\n"
+ " salq $" << csi.pos() << ", %rcx\n"
+ " popq %r9\n"
+ " addq %rcx, %r9\n";
+ }
+
+ for ( int c = 0; c < condKeySet.length(); c++ ) {
+ CondKey key = condKeySet[c];
+ out <<
+ " cmpq " << COND_KEY( key ) << ", %r9\n"
+ " je 102f\n";
+ }
+
+ out <<
+ " jmp " << LABEL( "pop_fail" ) << "\n"
+ "102:\n";
+ }
+ else {
+ CONDITION( ret, condition );
+ out <<
+ " test %eax, %eax\n"
+ " jz " << LABEL( "pop_fail" ) << "\n";
+ }
+}
+
+string AsmCodeGen::ERROR_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->errState != 0 )
+ ret << redFsm->errState->id;
+ else
+ ret << "-1";
+ return ret.str();
+}
+
+string AsmCodeGen::FIRST_FINAL_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->firstFinState != 0 )
+ ret << redFsm->firstFinState->id;
+ else
+ ret << redFsm->nextStateId;
+ return ret.str();
+}
+
+void AsmCodeGen::writeInit()
+{
+ if ( !noCS ) {
+ /* Don't use vCS here. vCS may assumes CS needs to be on the stack.
+ * Just use the interface register. */
+ out <<
+ " movq $" << redFsm->startState->id << ", %r11\n";
+ }
+
+ if ( redFsm->anyNfaStates() ) {
+ out <<
+ " movq $0, " << NFA_TOP() << "\n";
+ }
+
+ /* If there are any calls, then the stack top needs initialization. */
+ if ( redFsm->anyActionCalls() || redFsm->anyActionRets() ) {
+ out <<
+ " movq $0, " << TOP() << "\n";
+ }
+
+ if ( red->hasLongestMatch ) {
+ out <<
+ " movq $0, " << TOKSTART() << "\n"
+ " movq $0, " << TOKEND() << "\n"
+ " movq $0, " << ACT() << "\n";
+ }
+}
+
+string AsmCodeGen::DATA_PREFIX()
+{
+ if ( !noPrefix )
+ return FSM_NAME() + "_";
+ return "";
+}
+
+/* Emit the alphabet data type. */
+string AsmCodeGen::ALPH_TYPE()
+{
+ string ret = alphType->data1;
+ if ( alphType->data2 != 0 ) {
+ ret += " ";
+ ret += + alphType->data2;
+ }
+ return ret;
+}
+
+void AsmCodeGen::STATIC_CONST_INT( const string &name, const string &value )
+{
+ out <<
+ " .align 8\n"
+ " .type " << name << ", @object\n"
+ " .size " << name << ", 8\n" <<
+ name << ":\n"
+ " .long " << value << "\n";
+}
+
+void AsmCodeGen::STATE_IDS()
+{
+ if ( redFsm->startState != 0 )
+ STATIC_CONST_INT( START(), START_STATE_ID() );
+
+ if ( !noFinal )
+ STATIC_CONST_INT( FIRST_FINAL(), FIRST_FINAL_STATE() );
+
+ if ( !noError )
+ STATIC_CONST_INT( ERROR(), ERROR_STATE() );
+
+ out << "\n";
+
+ if ( red->entryPointNames.length() > 0 ) {
+ for ( EntryNameVect::Iter en = red->entryPointNames; en.lte(); en++ ) {
+ ostringstream ret;
+ ret << redFsm->startState->id;
+
+ STATIC_CONST_INT( string( DATA_PREFIX() + "en_" + *en ),
+ ret.str() );
+ }
+ out << "\n";
+ }
+}
+
+void AsmCodeGen::writeStart()
+{
+ out << START_STATE_ID();
+}
+
+void AsmCodeGen::writeFirstFinal()
+{
+ out << FIRST_FINAL_STATE();
+}
+
+void AsmCodeGen::writeError()
+{
+ out << ERROR_STATE();
+}
+
+string AsmCodeGen::PTR_CONST()
+{
+ return "const ";
+}
+
+string AsmCodeGen::PTR_CONST_END()
+{
+ return "";
+}
+
+std::ostream &AsmCodeGen::OPEN_ARRAY( string type, string name )
+{
+ out << "static const " << type << " " << name << "[] = {\n";
+ return out;
+}
+
+std::ostream &AsmCodeGen::CLOSE_ARRAY()
+{
+ return out << "};\n";
+}
+
+std::ostream &AsmCodeGen::STATIC_VAR( string type, string name )
+{
+ out << "static const " << type << " " << name;
+ return out;
+}
+
+string AsmCodeGen::UINT( )
+{
+ return "unsigned int";
+}
+
+string AsmCodeGen::ARR_OFF( string ptr, string offset )
+{
+ return ptr + " + " + offset;
+}
+
+string AsmCodeGen::CAST( string type )
+{
+ return "(" + type + ")";
+}
+
+string AsmCodeGen::NULL_ITEM()
+{
+ return "0";
+}
+
+string AsmCodeGen::POINTER()
+{
+ return " *";
+}
+
+std::ostream &AsmCodeGen::SWITCH_DEFAULT()
+{
+ return out;
+}
+
+string AsmCodeGen::CTRL_FLOW()
+{
+ return "";
+}
+
+void AsmCodeGen::writeExports()
+{
+ if ( red->exportList.length() > 0 ) {
+ for ( ExportList::Iter ex = red->exportList; ex.lte(); ex++ ) {
+ out << "#define " << DATA_PREFIX() << "ex_" << ex->name << " " <<
+ KEY(ex->key) << "\n";
+ }
+ out << "\n";
+ }
+}
+
+string AsmCodeGen::LABEL( const char *type, long i )
+{
+ std::stringstream s;
+ s << ".L" << red->machineId << "_" << type << "_" << i;
+ return s.str();
+}
+
+string AsmCodeGen::LABEL( const char *name )
+{
+ std::stringstream s;
+ s << ".L" << red->machineId << "_" << name;
+ return s.str();
+}
+
+void AsmCodeGen::emitSingleIfElseIf( RedStateAp *state )
+{
+ /* Load up the singles. */
+ int numSingles = state->outSingle.length();
+ RedTransEl *data = state->outSingle.data;
+
+ /* Write out the single indices. */
+ for ( int j = 0; j < numSingles; j++ ) {
+ out <<
+ " cmpb " << KEY( data[j].lowKey ) << ", %r10b\n"
+ " je " << TRANS_GOTO_TARG( data[j].value ) << "\n";
+ }
+}
+
+void AsmCodeGen::emitSingleJumpTable( RedStateAp *state, string def )
+{
+ int numSingles = state->outSingle.length();
+ RedTransEl *data = state->outSingle.data;
+
+ long long low = data[0].lowKey.getVal();
+ long long high = data[numSingles-1].lowKey.getVal();
+
+ if ( def.size() == 0 )
+ def = LABEL( "sjf", state->id );
+
+ out <<
+ " movzbq %r10b, %rax\n"
+ " subq $" << low << ", %rax\n"
+ " cmpq $" << (high - low) << ", %rax\n"
+ " ja " << def << "\n"
+ " leaq " << LABEL( "sjt", state->id ) << "(%rip), %rcx\n"
+ " movslq (%rcx,%rax,4), %rdx\n"
+ " addq %rcx, %rdx\n"
+ " jmp *%rdx\n"
+ " .section .rodata\n"
+ " .align 4\n"
+ << LABEL( "sjt", state->id ) << ":\n";
+
+ for ( long long j = 0; j < numSingles; j++ ) {
+ /* Fill in gap between prev and this. */
+ if ( j > 0 ) {
+ long long span = keyOps->span( data[j-1].lowKey, data[j].lowKey ) - 2;
+ for ( long long k = 0; k < span; k++ ) {
+ out << " .long " << def << " - " <<
+ LABEL( "sjt", state->id ) << "\n";
+ }
+ }
+
+ out << " .long " << TRANS_GOTO_TARG( data[j].value ) << " - " <<
+ LABEL( "sjt", state->id ) << "\n";
+ }
+
+ out <<
+ " .text\n"
+ "" << LABEL( "sjf", state->id ) << ":\n";
+}
+
+
+void AsmCodeGen::emitRangeBSearch( RedStateAp *state, int low, int high )
+{
+ static int nl = 1;
+
+ /* Get the mid position, staying on the lower end of the range. */
+ int mid = (low + high) >> 1;
+ RedTransEl *data = state->outRange.data;
+
+ /* Determine if we need to look higher or lower. */
+ bool anyLower = mid > low;
+ bool anyHigher = mid < high;
+
+ /* Determine if the keys at mid are the limits of the alphabet. */
+ bool limitLow = keyOps->eq( data[mid].lowKey, keyOps->minKey );
+ bool limitHigh = keyOps->eq( data[mid].highKey, keyOps->maxKey );
+
+// string nf = TRANS_GOTO_TARG( state->defTrans );
+
+ /* For some reason the hop is faster and results in smaller code. Not sure
+ * why. */
+ string nf = LABEL( "nf", state->id );
+
+ if ( anyLower && anyHigher ) {
+ int l1 = nl++;
+ string targ = TRANS_GOTO_TARG( data[mid].value );
+
+ /* Can go lower and higher than mid. */
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"
+ " jge " << LABEL( "nl", l1 ) << "\n";
+
+
+ emitRangeBSearch( state, low, mid-1 );
+
+ out <<
+ LABEL( "nl", l1 ) << ":\n";
+
+ if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) {
+ out <<
+ " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n";
+ }
+
+ out <<
+ " jle " << targ << "\n";
+
+ emitRangeBSearch( state, mid+1, high );
+ }
+ else if ( anyLower && !anyHigher ) {
+
+ string targ;
+ if ( limitHigh )
+ targ = TRANS_GOTO_TARG( data[mid].value );
+ else
+ targ = LABEL( "nl", nl++ );
+
+ /* Can go lower than mid but not higher. */
+
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"
+ " jge " << targ << "\n";
+
+ emitRangeBSearch( state, low, mid-1 );
+
+ /* If the higher is the highest in the alphabet then there is no sense
+ * testing it. */
+ if ( !limitHigh ) {
+
+ out <<
+ targ << ":\n";
+
+ if ( ! keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) {
+ out <<
+ " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n";
+ }
+
+ out <<
+ " jg " << nf << "\n";
+
+ TRANS_GOTO( data[mid].value );
+ }
+ }
+ else if ( !anyLower && anyHigher ) {
+ string targ;
+ if ( limitLow )
+ targ = TRANS_GOTO_TARG( data[mid].value );
+ else
+ targ = LABEL( "nl", nl++ );
+
+ /* Can go higher than mid but not lower. */
+
+ out <<
+ " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n"
+ " jle " << targ << "\n";
+
+ emitRangeBSearch( state, mid+1, high );
+
+ /* If the lower end is the lowest in the alphabet then there is no
+ * sense testing it. */
+ if ( !limitLow ) {
+
+ out <<
+ targ << ":\n";
+
+ if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) {
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n";
+ }
+
+ out <<
+ " jl " << nf << "\n";
+
+ TRANS_GOTO( data[mid].value );
+ }
+ }
+ else {
+ /* Cannot go higher or lower than mid. It's mid or bust. What
+ * tests to do depends on limits of alphabet. */
+ if ( !limitLow && !limitHigh ) {
+
+ if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) {
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"
+ " jl " << nf << "\n"
+ " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n"
+ " jg " << nf << "\n";
+ }
+ else {
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"
+ " jne " << nf << "\n";
+ }
+
+ TRANS_GOTO( data[mid].value );
+ }
+ else if ( limitLow && !limitHigh ) {
+
+ out <<
+ " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n"
+ " jg " << nf << "\n";
+
+ TRANS_GOTO( data[mid].value );
+ }
+ else if ( !limitLow && limitHigh ) {
+
+ out <<
+ " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"
+ " jl " << nf << "\n";
+
+ TRANS_GOTO( data[mid].value );
+ }
+ else {
+ /* Both high and low are at the limit. No tests to do. */
+ TRANS_GOTO( data[mid].value );
+ }
+ }
+}
+
+void AsmCodeGen::emitCharClassIfElseIf( RedStateAp *st )
+{
+ long long span = st->high - st->low + 1;
+ for ( long long pos = 0; pos < span; pos++ ) {
+ out <<
+ " cmpb " << KEY( st->low + pos ) << ", %r10b\n"
+ " je " << TRANS_GOTO_TARG( st->transList[pos] ) << "\n";
+ }
+}
+
+void AsmCodeGen::emitCharClassJumpTable( RedStateAp *st, string def )
+{
+ long long low = st->low;
+ long long high = st->high;
+
+ if ( def.size() == 0 )
+ def = LABEL( "ccf", st->id );
+
+ out <<
+ " movzbq %r10b, %rax\n"
+ " subq $" << low << ", %rax\n"
+ " cmpq $" << (high - low) << ", %rax\n"
+ " ja " << def << "\n"
+ " leaq " << LABEL( "cct", st->id ) << "(%rip), %rcx\n"
+ " movslq (%rcx,%rax,4), %rdx\n"
+ " addq %rcx, %rdx\n"
+ " jmp *%rdx\n"
+ " .section .rodata\n"
+ " .align 4\n"
+ << LABEL( "cct", st->id ) << ":\n";
+
+ long long span = st->high - st->low + 1;
+ for ( long long pos = 0; pos < span; pos++ ) {
+ out << " .long " << TRANS_GOTO_TARG( st->transList[pos] ) << " - " <<
+ LABEL( "cct", st->id ) << "\n";
+ }
+
+ out <<
+ " .text\n"
+ "" << LABEL( "ccf", st->id ) << ":\n";
+}
+
+void AsmCodeGen::NFA_PUSH( RedStateAp *st )
+{
+ if ( st->nfaTargs != 0 && st->nfaTargs->length() > 0 ) {
+ if ( red->nfaPrePushExpr != 0 ) {
+ out << " movq $" << st->nfaTargs->length() << ", %rdi\n";
+ INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false );
+ }
+
+ for ( RedNfaTargs::Iter t = *st->nfaTargs; t.lte(); t++ ) {
+ out <<
+ " movq " << NFA_STACK() << ", %rax\n"
+ " movq " << NFA_TOP() << ", %rcx\n"
+ " imulq $24, %rcx\n"
+ " movq $" << t->state->id << ", 0(%rax,%rcx,)\n"
+ " movq " << P() << ", 8(%rax,%rcx,)\n";
+
+ out <<
+ " # pop action id " << t->id << "\n"
+ " movq $" << t->id << ", 16(%rax,%rcx,)\n";
+
+ if ( t->push ) {
+ for ( GenActionTable::Iter item = t->push->key; item.lte(); item++ ) {
+ ACTION( out, item->value, st->id, false,
+ t->push->anyNextStmt() );
+ out << "\n";
+ }
+ }
+
+ out <<
+ " movq " << NFA_TOP() << ", %rcx\n"
+ " addq $1, %rcx\n"
+ " movq %rcx, " << NFA_TOP() << "\n";
+ }
+ }
+}
+
+void AsmCodeGen::STATE_GOTOS()
+{
+ bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates();
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Writing code above state gotos. */
+ IN_TRANS_ACTIONS( st );
+
+ if ( st->labelNeeded )
+ out << LABEL( "st", st->id ) << ":\n";
+
+
+ /* need to do this if the transition is an eof transition, or if the action
+ * contains fexec. Otherwise, no need. */
+ if ( eof ) {
+ out <<
+ " cmpq " << P() << ", " << vEOF() << "\n";
+
+ if ( st->isFinal )
+ out << " je " << LABEL( "out", st->id ) << "\n";
+ else
+ out << " je " << LABEL( "pop", st->id ) << "\n";
+ }
+
+ if ( st->toStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) {
+ ACTION( out, item->value, st->id, false,
+ st->toStateAction->anyNextStmt() );
+ out << "\n";
+ }
+ }
+
+ if ( st == redFsm->errState ) {
+ out << LABEL( "en", st->id ) << ":\n";
+
+ /* Break out here. */
+ outLabelUsed = true;
+
+ out <<
+ " movq $" << st->id << ", " << vCS() << "\n"
+ " jmp " << LABEL( "pop" ) << "\n";
+ }
+ else {
+ /* Advance and test buffer pos. */
+ if ( st->labelNeeded ) {
+ out <<
+ " addq $1, " << P() << "\n";
+
+ }
+
+ /* This is the entry label for starting a run. */
+ out << LABEL( "en", st->id ) << ":\n";
+
+ if ( !noEnd ) {
+ if ( eof ) {
+ out <<
+ " cmpq " << P() << ", " << PE() << "\n"
+ " jne " << LABEL( "nope", st->id ) << "\n" <<
+ " cmpq " << P() << ", " << vEOF() << "\n"
+ " jne " << LABEL( "out", st->id ) << "\n" <<
+ LABEL( "nope", st->id ) << ":\n";
+ }
+ else {
+ out <<
+ " cmpq " << P() << ", " << PE() << "\n"
+ " je " << LABEL( "out", st->id ) << "\n";
+ }
+ }
+
+ NFA_PUSH( st );
+
+ if ( st->fromStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ for ( GenActionTable::Iter item = st->fromStateAction->key;
+ item.lte(); item++ )
+ {
+ ACTION( out, item->value, st->id, false,
+ st->fromStateAction->anyNextStmt() );
+ out << "\n";
+ }
+ }
+
+ if ( !noEnd && eof ) {
+ out <<
+ " cmpq " << P() << ", " << vEOF() << "\n"
+ " jne " << LABEL( "neofd", st->id ) << "\n";
+
+ if ( st->eofTrans != 0 )
+ TRANS_GOTO( st->eofTrans );
+ else {
+ if ( st->isFinal || !redFsm->anyNfaStates() )
+ out << "jmp " << LABEL( "out", st->id ) << "\n";
+ else
+ out << "jmp " << LABEL( "pop", st->id ) << "\n";
+ }
+
+ out <<
+ " jmp " << LABEL( "deofd", st->id ) << "\n";
+
+ out << LABEL( "neofd", st->id ) << ":\n";
+ }
+
+ /* Record the prev state if necessary. */
+ if ( st->anyRegCurStateRef() ) {
+ out <<
+ " movq $" << st->id << ", -72(%rbp)\n";
+ }
+
+
+#ifdef LOG_TRANS
+ out <<
+ " movzbl (" << P() << "), %r10d\n"
+ " movq $" << machineId << ", %rdi\n"
+ " movq $" << st->id << ", %rsi\n"
+ " movslq %r10d, %rdx\n"
+ " call " << LABEL( "log_trans" ) << "\n"
+ ;
+#endif
+
+ /* Load *p. */
+ if ( st->transList != 0 ) {
+ long lowKey = redFsm->lowKey.getVal();
+ long highKey = redFsm->highKey.getVal();
+
+ out <<
+ " movzbl (" << P() << "), %r10d\n"
+ " cmpl $" << lowKey << ", %r10d\n"
+ " jl " << LABEL( "nf", st->id ) << "\n"
+ " cmpl $" << highKey << ", %r10d\n"
+ " jg " << LABEL( "nf", st->id ) << "\n"
+ " subl " << KEY( lowKey ) << ", %r10d\n"
+ " leaq " << LABEL( "char_class" ) << "(%rip), %rcx\n"
+ " movslq %r10d, %rax\n"
+ " movb (%rcx, %rax), %r10b\n"
+ ;
+
+
+ long len = ( st->high - st->low + 1 );
+
+ if ( len < 8 )
+ emitCharClassIfElseIf( st );
+ else {
+ string def;
+ if ( st->outRange.length() == 0 )
+ def = TRANS_GOTO_TARG( st->defTrans );
+ emitCharClassJumpTable( st, def );
+ }
+ }
+
+ /* Write the default transition. */
+ out << LABEL( "nf", st->id ) << ":\n";
+ TRANS_GOTO( st->defTrans );
+
+ if ( !noEnd && eof ) {
+ out << LABEL( "deofd", st->id) << ":\n";
+ }
+ }
+ }
+}
+
+unsigned int AsmCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ return act;
+}
+
+unsigned int AsmCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ return act;
+}
+
+unsigned int AsmCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ return act;
+}
+
+bool AsmCodeGen::useAgainLabel()
+{
+ return redFsm->anyActionRets() ||
+ redFsm->anyActionByValControl() ||
+ redFsm->anyRegNextStmt();
+}
+
+void AsmCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret <<
+ " jmp " << LABEL( "st", gotoDest ) << "\n";
+}
+
+void AsmCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ if ( red->prePushExpr != 0 )
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+
+ ret <<
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " movq $" << targState << ", (%rax, %rcx, 8)\n"
+ " addq $1, %rcx\n"
+ " movq %rcx, " << TOP() << "\n"
+ ;
+
+ ret <<
+ " jmp " << LABEL( "st", callDest ) << "\n";
+ ;
+}
+
+void AsmCodeGen::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish )
+{
+ if ( red->prePushExpr != 0 )
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+
+ ret <<
+ "\n"
+ " movq ";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << ", %rdx\n"
+ "\n"
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " movq $" << targState << ", (%rax, %rcx, 8)\n"
+ " addq $1, %rcx\n"
+ " movq %rcx, " << TOP() << "\n"
+ " movq %rdx, " << vCS() << "\n"
+ ;
+
+ ret <<
+ " jmp " << LABEL( "again" ) << "\n";
+}
+
+void AsmCodeGen::RET( ostream &ret, bool inFinish )
+{
+ ret <<
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " subq $1, %rcx\n"
+ " movq (%rax, %rcx, 8), %rax\n"
+ " movq %rax, " << vCS() << "\n"
+ " movq %rcx, " << TOP() << "\n";
+
+ if ( red->postPopExpr != 0 )
+ INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false );
+
+ ret <<
+ " jmp " << LABEL("again") << "\n";
+}
+
+void AsmCodeGen::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
+{
+ ret << " movq ";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << ", " << vCS() << "\n";
+
+ ret <<
+ " jmp " << LABEL("again") << "\n";
+}
+
+void AsmCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret <<
+ " movq $" << nextDest << ", " << vCS() << "\n";
+}
+
+void AsmCodeGen::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
+{
+ ret << " movq ";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << ", " << vCS() << "\n";
+}
+
+void AsmCodeGen::NCALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ if ( red->prePushExpr != 0 )
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+
+ ret <<
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " movq $" << targState << ", (%rax, %rcx, 8)\n"
+ " addq $1, %rcx\n"
+ " movq %rcx, " << TOP() << "\n"
+ " movq $" << callDest << ", " << vCS() << "\n";
+}
+
+void AsmCodeGen::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem,
+ int targState, bool inFinish )
+{
+ if ( red->prePushExpr != 0 )
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+
+ ret <<
+ "\n"
+ " movq ";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << ", %rdx\n"
+ "\n"
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " movq $" << targState << ", (%rax, %rcx, 8)\n"
+ " addq $1, %rcx\n"
+ " movq %rcx, " << TOP() << "\n"
+ " movq %rdx, " << vCS() << "\n";
+}
+
+void AsmCodeGen::NRET( ostream &ret, bool inFinish )
+{
+ ret <<
+ " movq " << STACK() << ", %rax\n"
+ " movq " << TOP() << ", %rcx\n"
+ " subq $1, %rcx\n"
+ " movq (%rax, %rcx, 8), %rax\n"
+ " movq %rax, " << vCS() << "\n"
+ " movq %rcx, " << TOP() << "\n";
+
+ if ( red->postPopExpr != 0 )
+ INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false );
+}
+
+void AsmCodeGen::CURS( ostream &ret, bool inFinish )
+{
+ ret <<
+ " movq -72(%rbp), %rax\n";
+}
+
+void AsmCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret <<
+ " movq $" << targState << ", %rax\n";
+}
+
+void AsmCodeGen::BREAK( ostream &ret, int targState, bool csForced )
+{
+ outLabelUsed = true;
+ ret << "{" << P() << "++; ";
+ if ( !csForced )
+ ret << vCS() << " = " << targState << "; ";
+ ret << CTRL_FLOW() << "goto _out;}";
+}
+
+bool AsmCodeGen::IN_TRANS_ACTIONS( RedStateAp *state )
+{
+ bool anyWritten = false;
+
+ /* Emit any transitions that have actions and that go to this state. */
+ for ( int it = 0; it < state->numInCondTests; it++ ) {
+ /* Write the label for the transition so it can be jumped to. */
+ RedTransAp *trans = state->inCondTests[it];
+ out << LABEL( "ctr", trans->id ) << ":\n";
+
+ if ( trans->condSpace->condSet.length() == 1 ) {
+ RedCondPair *tp, *fp;
+ if ( trans->numConds() == 1 ) {
+ /* The single condition is either false or true, errCond is the
+ * opposite. */
+ if ( trans->outCondKey(0) == 0 ) {
+ fp = trans->outCond(0);
+ tp = trans->errCond();
+ }
+ else {
+ tp = trans->outCond(0);
+ fp = trans->errCond();
+ }
+ }
+ else {
+ /* Full list, goes false, then true. */
+ fp = trans->outCond(0);
+ tp = trans->outCond(1);
+ }
+
+ GenCondSet::Iter csi = trans->condSpace->condSet;
+ CONDITION( out, *csi );
+
+ out <<
+ " test %eax, %eax\n"
+ " je " << TRANS_GOTO_TARG( fp ) << "\n"
+ " jmp " << TRANS_GOTO_TARG( tp ) << "\n";
+ }
+ else {
+ out << " movq $0, %r9\n";
+
+ for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) {
+ out <<
+ " pushq %r9\n";
+
+ CONDITION( out, *csi );
+ out <<
+ "\n"
+ " test %eax, %eax\n"
+ " setne %cl\n"
+ " movsbq %cl, %rcx\n"
+ " salq $" << csi.pos() << ", %rcx\n"
+ " popq %r9\n"
+ " addq %rcx, %r9\n";
+ }
+
+ for ( int c = 0; c < trans->numConds(); c++ ) {
+ CondKey key = trans->outCondKey( c );
+ RedCondPair *pair = trans->outCond( c );
+ out <<
+ " cmpq " << COND_KEY( key ) << ", %r9\n"
+ " je " << TRANS_GOTO_TARG( pair ) << "\n";
+
+ }
+
+ RedCondPair *err = trans->errCond();
+ if ( err != 0 ) {
+ out <<
+ " jmp " << TRANS_GOTO_TARG( err ) << "\n";
+ }
+ }
+ }
+
+ /* Emit any transitions that have actions and that go to this state. */
+ for ( int it = 0; it < state->numInConds; it++ ) {
+ RedCondPair *pair = state->inConds[it];
+ if ( pair->action != 0 /* && pair->labelNeeded */ ) {
+ /* Remember that we wrote an action so we know to write the
+ * line directive for going back to the output. */
+ anyWritten = true;
+
+ /* Write the label for the transition so it can be jumped to. */
+ out << LABEL( "tr", pair->id ) << ":\n";
+
+ /* If the action contains a next, then we must preload the current
+ * state since the action may or may not set it. */
+ if ( pair->action->anyNextStmt() ) {
+ out <<
+ " movq $" << pair->targ->id << ", " << vCS() << "\n";
+ }
+
+ if ( redFsm->anyRegNbreak() ) {
+ out <<
+ " movb $0, " << NBREAK() << "\n";
+ }
+
+ /* Write each action in the list. */
+ for ( GenActionTable::Iter item = pair->action->key; item.lte(); item++ ) {
+ ACTION( out, item->value, pair->targ->id, false,
+ pair->action->anyNextStmt() );
+ out << "\n";
+ }
+
+ if ( redFsm->anyRegNbreak() ) {
+ out <<
+ " cmpb $0, " << NBREAK() << "\n"
+ " jne " << LABEL( "pop" ) << "\n";
+ outLabelUsed = true;
+ }
+
+
+ /* If the action contains a next then we need to reload, otherwise
+ * jump directly to the target state. */
+ if ( pair->action->anyNextStmt() )
+ out << " jmp " << LABEL( "again" ) << "\n";
+ else
+ out << " jmp " << LABEL( "st", pair->targ->id ) << "\n";
+ }
+ }
+
+ return anyWritten;
+}
+
+std::string AsmCodeGen::TRANS_GOTO_TARG( RedCondPair *pair )
+{
+ std::stringstream s;
+ if ( pair->action != 0 ) {
+ /* Go to the transition which will go to the state. */
+ s << LABEL( "tr", pair->id );
+ }
+ else {
+ /* Go directly to the target state. */
+ s << LABEL( "st", pair->targ->id );
+ }
+ return s.str();
+}
+
+std::string AsmCodeGen::TRANS_GOTO_TARG( RedTransAp *trans )
+{
+ if ( trans->condSpace != 0 ) {
+ /* Need to jump to the trans since there are conditions. */
+ return LABEL( "ctr", trans->id );
+ }
+ else {
+ return TRANS_GOTO_TARG( &trans->p );
+ }
+}
+
+/* Emit the goto to take for a given transition. */
+std::ostream &AsmCodeGen::TRANS_GOTO( RedTransAp *trans )
+{
+ out << " jmp " << TRANS_GOTO_TARG( trans ) << "\n";
+ return out;
+}
+
+std::ostream &AsmCodeGen::EXIT_STATES()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ out <<
+ LABEL( "out", st->id ) << ":\n"
+ " movq $" << st->id << ", " << vCS() << "\n"
+ " jmp " << LABEL( "out" ) << "\n";
+
+ out <<
+ LABEL( "pop", st->id ) << ":\n"
+ " movq $" << st->id << ", " << vCS() << "\n"
+ " jmp " << LABEL( "pop" ) << "\n";
+ }
+ return out;
+}
+
+std::ostream &AsmCodeGen::AGAIN_CASES()
+{
+ /* Jump into the machine based on the current state. */
+ out <<
+ " leaq " << LABEL( "again_jmp" ) << "(%rip), %rcx\n";
+
+ if ( stackCS ) {
+ out <<
+ " movq " << vCS() << ", %r11\n";
+ }
+
+ out <<
+ " movq (%rcx,%r11,8), %rcx\n"
+ " jmp *%rcx\n"
+ " .section .rodata\n"
+ " .align 8\n"
+ << LABEL( "again_jmp" ) << ":\n";
+
+ for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) {
+ out <<
+ " .quad " << LABEL( "st", stId ) << "\n";
+ }
+
+ out <<
+ " .text\n";
+
+ return out;
+}
+
+std::ostream &AsmCodeGen::ENTRY_CASES()
+{
+ out <<
+ " movq (%rcx,%r11,8), %rcx\n"
+ " jmp *%rcx\n"
+ " .section .rodata\n"
+ " .align 8\n"
+ << LABEL( "entry_jmp" ) << ":\n";
+
+ for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) {
+ out <<
+ " .quad " << LABEL( "en", stId ) << "\n";
+ }
+
+ out <<
+ " .text\n";
+ return out;
+}
+
+
+std::ostream &AsmCodeGen::FINISH_CASES()
+{
+ /* The current state is in %rax. */
+ /*long done = */ nextLmSwitchLabel++;
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->eofTrans != 0 ) {
+ out <<
+ " cmpq $" << st->id << ", %rax\n"
+ " jne " << LABEL( "fc", st->id ) << "\n";
+
+ if ( st->fromStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ for ( GenActionTable::Iter item = st->fromStateAction->key;
+ item.lte(); item++ )
+ {
+ ACTION( out, item->value, st->id, false,
+ st->fromStateAction->anyNextStmt() );
+ out << "\n";
+ }
+ }
+
+ out <<
+ " jmp " << TRANS_GOTO_TARG( st->eofTrans ) << "\n" <<
+ LABEL( "fc", st->id ) << ":\n";
+ }
+ }
+
+ return out;
+}
+
+void AsmCodeGen::setLabelsNeeded( GenInlineList *inlineList )
+{
+ for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case GenInlineItem::Goto: case GenInlineItem::Call: {
+ /* Mark the target as needing a label. */
+ item->targState->labelNeeded = true;
+ break;
+ }
+ default: break;
+ }
+
+ if ( item->children != 0 )
+ setLabelsNeeded( item->children );
+ }
+}
+
+void AsmCodeGen::setLabelsNeeded( RedCondPair *pair )
+{
+ /* If there is no action with a next statement, then the label will be
+ * needed. */
+ if ( pair->action == 0 || !pair->action->anyNextStmt() )
+ pair->targ->labelNeeded = true;
+
+ /* Need labels for states that have goto or calls in action code
+ * invoked on characters (ie, not from out action code). */
+ if ( pair->action != 0 ) {
+ /* Loop the actions. */
+ for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) {
+ /* Get the action and walk it's tree. */
+ setLabelsNeeded( act->value->inlineList );
+ }
+ }
+}
+
+/* Set up labelNeeded flag for each state. */
+void AsmCodeGen::setLabelsNeeded()
+{
+ /* If we use the _again label, then we the _again switch, which uses all
+ * labels. */
+ if ( useAgainLabel() ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = true;
+ }
+ else {
+ /* Do not use all labels by default, init all labelNeeded vars to false. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = false;
+
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ if ( trans->condSpace == 0 )
+ setLabelsNeeded( &trans->p );
+ }
+
+ for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ )
+ setLabelsNeeded( &cond->p );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->eofAction != 0 ) {
+ for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ )
+ setLabelsNeeded( item->value->inlineList );
+ }
+ }
+ }
+
+ if ( !noEnd ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->outNeeded = st->labelNeeded;
+ }
+}
+
+void AsmCodeGen::writeData()
+{
+ STATE_IDS();
+
+ long long maxSpan = keyOps->span( redFsm->lowKey, redFsm->highKey );
+
+ out <<
+ " .type " << LABEL( "char_class" ) << ", @object\n" <<
+ LABEL( "char_class" ) << ":\n";
+
+ for ( long long pos = 0; pos < maxSpan; pos++ ) {
+ out <<
+ " .byte " << redFsm->classMap[pos] << "\n";
+ }
+
+#ifdef LOG_TRANS
+ out <<
+ LABEL( "fmt_log_trans" ) << ":\n"
+ " .string \"%i %i %i\\n\"\n";
+#endif
+}
+
+void AsmCodeGen::setNfaIds()
+{
+ long nextId = 1;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs != 0 ) {
+ for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) {
+ targ->id = nextId;
+ nextId += 1;
+ }
+ }
+ }
+}
+
+void AsmCodeGen::writeExec()
+{
+ /* Must set labels immediately before writing because we may depend on the
+ * noend write option. */
+ setLabelsNeeded();
+ testEofUsed = false;
+ outLabelUsed = false;
+
+ setNfaIds();
+
+ /* If there are eof actions then we need to run code after exporting the
+ * final state to vCS. Since the interface register is calee-save, we need
+ * it to live on the stack. */
+ stackCS = redFsm->anyEofActivity();
+
+ /*
+ * This code needs 88 bytes of stack (offset 0 from %rbp).
+ *
+ * cv : %r9 -- caller-save, used internally, condition char, undefined in
+ * conditions and actions, can use
+ *
+ * pc : %r10b -- caller-save, used internally, undefined in conditions
+ * actions, can use
+ *
+ * cs : %r11 -- caller-save, written by write init, read and
+ * written by exec, undefined in conditions and actions
+ *
+ * p : %r12 -- callee-save, interface, persistent
+ *
+ * pe : %r13 -- callee-save, interface, persistent
+ *
+ * eof: -8(%rbp)
+ *
+ * ts: -16(%rbp)
+ *
+ * te: -24(%rbp)
+ *
+ * act: -32(%rbp)
+ *
+ * _nbreak: -40(%rbp)
+ *
+ * stackCS: -48(%rbp)
+ *
+ * stack: -56(%rbp)
+ * top: -64(%rbp)
+ *
+ * _ps: -72(%rbp)
+ *
+ * nfa_stack -80(%rbp)
+ * nfa_top -88(%rbp)
+ * nfa_sz -96(%rbp)
+ */
+
+ if ( redFsm->anyRegCurStateRef() ) {
+ out <<
+ " movq $0, -72(%rbp)\n";
+ }
+
+ if ( stackCS ) {
+ /* Only need a persistent cs in the case of eof actions when exiting the
+ * block. Where CS lives is a matter of performance though, so we should
+ * only do this if necessary. */
+ out <<
+ " movq %r11, " << vCS() << "\n";
+ }
+
+ if ( useAgainLabel() ) {
+ out <<
+ " jmp " << LABEL( "resume" ) << "\n"
+ << LABEL( "again" ) << ":\n";
+
+ AGAIN_CASES();
+ }
+
+ if ( useAgainLabel() || redFsm->anyNfaStates() )
+ out << LABEL( "resume" ) << ":\n";
+
+ /* Jump into the machine based on the current state. */
+ out <<
+ " leaq " << LABEL( "entry_jmp" ) << "(%rip), %rcx\n";
+
+ if ( stackCS ) {
+ out <<
+ " movq " << vCS() << ", %r11\n";
+ }
+
+ ENTRY_CASES();
+
+ STATE_GOTOS();
+
+ EXIT_STATES();
+
+ out << LABEL( "pop" ) << ":\n";
+
+ if ( redFsm->anyNfaStates() ) {
+ out <<
+ " movq " << NFA_TOP() << ", %rcx\n"
+ " cmpq $0, %rcx\n"
+ " je " << LABEL( "nfa_stack_empty" ) << "\n"
+ " movq " << NFA_TOP() << ", %rcx\n"
+ " subq $1, %rcx\n"
+ " movq %rcx, " << NFA_TOP() << "\n"
+ " movq " << NFA_STACK() << ", %rax\n"
+ " imulq $24, %rcx\n"
+ " movq 0(%rax,%rcx,), %r11\n"
+ " movq 8(%rax,%rcx,), " << P() << "\n"
+ " movq %r11, " << vCS() << "\n"
+ ;
+
+ if ( redFsm->bAnyNfaPops ) {
+ out <<
+ " movq %r11, %r14\n"
+ " movq 16(%rax,%rcx,), %rax\n";
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs != 0 ) {
+ for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) {
+
+ /* Write the entry label. */
+ out <<
+ " # pop action select\n"
+ " cmp $" << targ->id << ", %rax\n"
+ " jne 100f\n";
+
+ if ( targ->popTest != 0 ) {
+ /* Write each action in the list of action items. */
+ for ( GenActionTable::Iter item = targ->popTest->key; item.lte(); item++ )
+ NFA_CONDITION( out, item->value, item.last() );
+ }
+
+ out <<
+ " jmp 101f\n"
+ "100:\n";
+
+ }
+ }
+ }
+
+ out <<
+ "101:\n"
+ " movq %r14, %r11\n";
+ }
+
+ out <<
+ " jmp " << LABEL( "resume" ) << "\n" <<
+ LABEL( "pop_fail" ) << ":\n"
+ " movq $" << ERROR_STATE() << ", " << vCS() << "\n"
+ " jmp " << LABEL( "resume" ) << "\n" <<
+ LABEL( "nfa_stack_empty" ) << ":\n";
+ }
+
+ if ( stackCS ) {
+ out <<
+ " movq " << vCS() << ", %r11\n";
+ }
+
+ out <<
+ "# WRITE EXEC END\n";
+
+ out << LABEL( "out" ) << ":\n";
+
+ if ( stackCS ) {
+ out <<
+ " movq " << vCS() << ", %r11\n";
+ }
+
+#ifdef LOG_TRANS
+ out <<
+ " jmp " << LABEL( "skip" ) << "\n" <<
+ LABEL( "log_trans" ) << ":\n"
+ " movq %rdx, %rcx\n"
+ " movq %rsi, %rdx\n"
+ " movq %rdi, %rsi\n"
+ " movq " << LABEL( "fmt_log_trans" ) << "@GOTPCREL(%rip), %rdi\n"
+ " movq $0, %rax\n"
+ " call printf@PLT\n"
+ " ret\n" <<
+ LABEL( "skip" ) << ":\n"
+ "\n";
+#endif
+}