#include "compiler/InlineAsm.h" #include "compiler/InlineAsmPPC.h" #include "compiler/GCCInlineAsm.h" #include "compiler/CompilerTools.h" #include "compiler/CError.h" #include "compiler/CExpr.h" #include "compiler/CFunc.h" #include "compiler/CInit.h" #include "compiler/CInline.h" #include "compiler/CInt64.h" #include "compiler/CMachine.h" #include "compiler/COptimizer.h" #include "compiler/CParser.h" #include "compiler/CPrep.h" #include "compiler/CPrepTokenizer.h" #include "compiler/CScope.h" #include "compiler/PCode.h" #include "compiler/Registers.h" #include "compiler/objects.h" #include "compiler/scopes.h" #include "compiler/types.h" int allow_array_expressions = 1; int backtracking; jmp_buf backtrack; jmp_buf InlineAsm_assemblererror; static int ASMstmtnb; void AssemblerError(void) { longjmp(InlineAsm_assemblererror, 1); } void InlineAsm_SyntaxError(short code) { if (backtracking) longjmp(backtrack, 1); if (tk == TK_EOL || tk == ';') code = CErrorStr112; CError_Error(code); } CLabel *InlineAsm_LookupLabel(HashNameNode *name) { CLabel *label; for (label = Labels; label; label = label->next) { if (name == label->name) break; } return label; } CLabel *InlineAsm_DeclareLabel(HashNameNode *name) { CLabel *label = newlabel(); label->name = name; label->next = Labels; Labels = label; return label; } static void InlineAsm_DefineLabel(HashNameNode *name) { CLabel *label; Statement *stmt; label = InlineAsm_LookupLabel(name); if (!label) { label = InlineAsm_DeclareLabel(name); } else { if (label->stmt) CError_Error(CErrorStr171, name->name); } stmt = CFunc_AppendStatement(ST_LABEL); stmt->label = label; label->stmt = stmt; } Boolean InlineAsm_LookupSymbolOrTag(HashNameNode *name, IALookupResult *result, Boolean allow_tag) { ObjBase *obj; NameSpace *nspace; NameSpaceObjectList *list; result->name = name; result->object = NULL; result->label = NULL; result->type = NULL; result->has_value = 0; if ((result->label = InlineAsm_LookupLabel(name))) return 1; for (nspace = cscope_current; nspace; nspace = nspace->parent) { if ((list = CScope_FindName(nspace, name))) { obj = list->object; switch (obj->otype) { case OT_ENUMCONST: result->has_value = 1; result->value = OBJ_ENUM_CONST(list->object)->val.lo; return 1; case OT_OBJECT: if (OBJECT(obj)->datatype == DABSOLUTE) { result->has_value = 1; result->value = OBJECT(obj)->u.address; } else { if (OBJECT(obj)->datatype == DDATA && (OBJECT(obj)->qual & Q_10000)) CInit_ExportConst(OBJECT(obj)); result->object = OBJECT(obj); } return 1; case OT_TYPE: result->type = OBJ_TYPE(obj)->type; return 1; case OT_TYPETAG: if (allow_tag) { result->type = OBJ_TYPE_TAG(obj)->type; return 1; } case OT_NAMESPACE: case OT_MEMBERVAR: return 0; default: CError_FATAL(245); } } } return 0; } Boolean InlineAsm_LookupSymbol(HashNameNode *name, IALookupResult *result) { return InlineAsm_LookupSymbolOrTag(name, result, 0); } static ObjMemberVar *isclassmember(TypeClass *tclass, HashNameNode *name) { NameSpaceObjectList *list; list = CScope_FindName(tclass->nspace, name); return (list && list->object->otype == OT_MEMBERVAR) ? OBJ_MEMBER_VAR(list->object) : NULL; } SInt32 InlineAsm_StructMemberOffset(Type *type) { StructMember *member; ObjMemberVar *ivar; SInt32 offset = 0; do { if (IS_TYPE_STRUCT(type)) { tk = lex(); if (tk != TK_IDENTIFIER) InlineAsm_SyntaxError(CErrorStr107); member = ismember(TYPE_STRUCT(type), tkidentifier); if (!member) CError_Error(CErrorStr150, tkidentifier->name); offset += member->offset; type = member->type; tk = lex(); } else if (IS_TYPE_CLASS(type)) { tk = lex(); if (tk != TK_IDENTIFIER) InlineAsm_SyntaxError(CErrorStr107); ivar = isclassmember(TYPE_CLASS(type), tkidentifier); if (!ivar) CError_Error(CErrorStr150, tkidentifier->name); offset += ivar->offset; type = ivar->type; tk = lex(); } else { CError_Error(CErrorStr149); } } while (tk == '.'); return offset; } SInt32 InlineAsm_StructArrayMemberOffset(Type *type) { StructMember *member; ObjMemberVar *ivar; SInt32 offset = 0; do { if (tk == '.') { if (IS_TYPE_STRUCT(type)) { tk = lex(); if (tk != TK_IDENTIFIER) InlineAsm_SyntaxError(CErrorStr107); member = ismember(TYPE_STRUCT(type), tkidentifier); if (!member) CError_Error(CErrorStr150, tkidentifier->name); offset += member->offset; type = member->type; tk = lex(); } else if (IS_TYPE_CLASS(type)) { tk = lex(); if (tk != TK_IDENTIFIER) InlineAsm_SyntaxError(CErrorStr107); ivar = isclassmember(TYPE_CLASS(type), tkidentifier); if (!ivar) CError_Error(CErrorStr150, tkidentifier->name); offset += ivar->offset; type = ivar->type; tk = lex(); } else { CError_Error(CErrorStr149); } } else { if (IS_TYPE_ARRAY(type)) { type = TPTR_TARGET(type); tk = lex(); offset += type->size * InlineAsm_ConstantExpression(); if (tk != ']') InlineAsm_SyntaxError(125); tk = lex(); } else { CError_Error(CErrorStr148); } } } while (tk == '.' || tk == '['); return offset; } SInt32 InlineAsm_StructPointerMemberOffset(Type *type) { StructMember *member; ObjMemberVar *ivar; SInt32 offset; tk = lex(); if (tk != TK_IDENTIFIER) InlineAsm_SyntaxError(107); if (IS_TYPE_STRUCT(type)) { member = ismember(TYPE_STRUCT(type), tkidentifier); if (!member) CError_Error(CErrorStr150, tkidentifier->name); offset = member->offset; type = member->type; } else { ivar = isclassmember(TYPE_CLASS(type), tkidentifier); if (!ivar) CError_Error(CErrorStr150, tkidentifier->name); offset = ivar->offset; type = ivar->type; } tk = lex(); if (tk == '.' || tk == '[') offset += InlineAsm_StructArrayMemberOffset(type); return offset; } static SInt32 DiadicOperator(SInt32 left, short op, SInt32 right) { CInt64 left64; CInt64 right64; CInt64_SetLong(&left64, left); CInt64_SetLong(&right64, right); right64 = CMach_CalcIntDiadic(TYPE(&stsignedint), left64, op, right64); return CInt64_GetULong(&right64); } static SInt32 PrimaryExpression(void) { IALookupResult result; SInt32 value; switch (tk) { case TK_IDENTIFIER: if (InlineAsm_LookupSymbol(tkidentifier, &result)) { if (result.has_value) { tk = lex(); return result.value; } if (result.type && (IS_TYPE_STRUCT(result.type) || IS_TYPE_CLASS(result.type))) { tk = lex(); if (tk != '.') InlineAsm_SyntaxError(120); if (allow_array_expressions) return InlineAsm_StructArrayMemberOffset(result.type); else return InlineAsm_StructMemberOffset(result.type); } else { InlineAsm_SyntaxError(124); } } else { InlineAsm_SyntaxError(124); } break; case TK_INTCONST: value = tkintconst.lo; tk = lex(); return value; case TK_SIZEOF: return scansizeof(); case '+': tk = lex(); return PrimaryExpression(); case '-': tk = lex(); return -PrimaryExpression(); case '!': tk = lex(); return PrimaryExpression() == 0; case '~': tk = lex(); return ~PrimaryExpression(); case '(': tk = lex(); value = InlineAsm_ConstantExpression(); if (tk != ')') InlineAsm_SyntaxError(115); tk = lex(); return value; default: InlineAsm_SyntaxError(120); } return 0; } static SInt32 ConstantExpressionTail(SInt32 value) { SInt32 right; short left_token; short right_prec; while (1) { left_token = tk; tk = lex(); right = PrimaryExpression(); right_prec = GetPrec(tk); if (right_prec == 0) return DiadicOperator(value, left_token, right); if (GetPrec(left_token) >= right_prec) { value = DiadicOperator(value, left_token, right); } else { value = DiadicOperator(value, left_token, ConstantExpressionTail(right)); if (GetPrec(tk) == 0) return value; } } } SInt32 InlineAsm_ConstantExpression(void) { SInt32 value = PrimaryExpression(); if (GetPrec(tk) == 0) return value; else return ConstantExpressionTail(value); } HashNameNode *MakeLocalLabel(CInt64 num) { char buf[80]; sprintf(buf, "@%i_%i", ASMstmtnb, CInt64_GetULong(&num)); return GetHashNameNodeExport(buf); } static void ScanOptionalLabel(void) { if (tk == TK_INTCONST) { if (lookahead() == ':') { InlineAsm_DefineLabel(MakeLocalLabel(tkintconst)); tk = lex(); tk = lex(); } } else { if (tkidentifier->name[0] == '@') { InlineAsm_DefineLabel(tkidentifier); tk = lex(); if (tk == ':') tk = lex(); } else { HashNameNode *name = tkidentifier; short t = lookahead(); tkidentifier = name; if (t == ':') { InlineAsm_DefineLabel(name); tk = lex(); tk = lex(); } } } } static void ScanStatements(volatile short endToken, AssemblerType mode) { if (setjmp(InlineAsm_assemblererror)) { while (tk != TK_EOL && tk != endToken && tk != '}' && tk) tk = lex(); if (tk == ';' || tk == TK_EOL) tk = lex(); } else { InlineAsm_Initialize(mode); InlineAsm_gccmode = 0; if (setjmp(InlineAsm_assemblererror)) { while (tk != ';' && tk != TK_EOL && tk != endToken && tk != '}' && tk) tk = lex(); if (tk == ';' || tk == TK_EOL) tk = lex(); } while (tk && tk != endToken) { backtracking = 0; sourceoffset = CPrep_GetFileOffsetInfo(&cparser_fileoffset); if (tk == '"') { if (InlineAsm_gccmode) { tk = lex(); InlineAsm_gcc_parse(); } else { InlineAsm_gccmode = 1; copts.cplusplus = 0; copts.asmpoundcomment = 1; tk = lex(); } } if (tk == '.') { InlineAsm_ScanAssemblyDirective(); } else if (tk == TK_IDENTIFIER) { ScanOptionalLabel(); if (tk == TK_IDENTIFIER) InlineAsm_ScanAssemblyInstruction(); } else if (tk == TK_INTCONST) { ScanOptionalLabel(); if (tk == TK_IDENTIFIER) InlineAsm_ScanAssemblyInstruction(); } if (InlineAsm_gccmode && tk == '"') { tk = lex(); InlineAsm_gcc_parse(); } if (tk == ';' || tk == TK_EOL) { CPrep_TokenStreamFlush(); tk = lex(); } else if (tk != endToken) { if (endToken == ')') CError_Error(CErrorStr115); else CError_Error(CErrorStr113); } } } } void InlineAsm_ScanStatements(volatile short endToken) { ScanStatements(endToken, AssemblerType_0); } void InlineAsm_ScanFunction(volatile short endToken) { ScanStatements(endToken, AssemblerType_1); } void InlineAsm_Assemble(void) { short token = (tk == '(') ? ')' : '}'; char save_pc = copts.asmpoundcomment; char save_cpp = copts.cplusplus; cprep_nostring = 1; CFunc_AppendStatement(ST_NOP); first_ST_ASM = curstmt; ASMstmtnb++; cprep_eoltokens = 1; in_assembler = 1; tk = lex(); InlineAsm_ScanStatements(token); in_assembler = 0; cprep_eoltokens = 0; cprep_nostring = 0; copts.asmpoundcomment = save_pc; copts.cplusplus = save_cpp; } void InlineAsm_PackAsmStatement(Statement *stmt, Statement *first, void **output, SInt32 *outsize) { InlineAsm *src; InlineAsm *dest; IAOperand *op; SInt32 i; SInt32 size; src = (InlineAsm *) stmt->expr; size = sizeof(InlineAsm) + sizeof(IAOperand) * src->argcount; dest = galloc(size); memcpy(dest, src, size); for (i = 0, op = dest->args; i < dest->argcount; i++, op++) { switch (op->type) { case IAOpnd_0: break; case IAOpnd_Reg: case IAOpnd_4: op->u.reg.object = (Object *) CInline_GetLocalID(op->u.reg.object); break; case IAOpnd_Lab: op->u.lab.label = (CLabel *) CInline_GetStatementNumber(first, op->u.lab.label->stmt); break; case IAOpnd_LabDiff: op->u.labdiff.label1 = (CLabel *) CInline_GetStatementNumber(first, op->u.labdiff.label1->stmt); op->u.labdiff.label2 = (CLabel *) CInline_GetStatementNumber(first, op->u.labdiff.label2->stmt); break; } } *output = dest; *outsize = size; } void InlineAsm_UnpackAsmStatement(Statement *stmt, CLabel **labelArray, Boolean flag, void *data, SInt32 size) { InlineAsm *ia; IAOperand *op; SInt32 i; ia = galloc(size); memcpy(ia, data, size); for (i = 0, op = ia->args; i < ia->argcount; i++, op++) { switch (op->type) { case IAOpnd_0: break; case IAOpnd_Reg: case IAOpnd_4: op->u.reg.object = CInline_GetLocalObj((SInt32) op->u.reg.object, flag); break; case IAOpnd_Lab: op->u.lab.label = labelArray[(SInt16) op->u.lab.label]; break; case IAOpnd_LabDiff: op->u.labdiff.label1 = labelArray[(SInt16) op->u.labdiff.label1]; op->u.labdiff.label2 = labelArray[(SInt16) op->u.labdiff.label2]; break; } } stmt->expr = (ENode *) ia; } void InlineAsm_CheckLocalUsage(Statement *stmt) { InlineAsm *ia = (InlineAsm *) stmt->expr; IAOperand *op; SInt32 i; for (i = 0, op = ia->args; i < ia->argcount; i++, op++) { switch (op->type) { case IAOpnd_Reg: if (op->u.reg.object) SetVarUsage(op->u.reg.object, 0); break; case IAOpnd_4: SetVarUsage(op->u.obj.obj, 1); break; } } } CLabel *InlineAsm_GetReferencedLabel(Statement *stmt) { InlineAsm *ia = (InlineAsm *) stmt->expr; IAOperand *op; SInt32 i; for (i = 0, op = ia->args; i < ia->argcount; i++, op++) { if (op->type == IAOpnd_Lab) return op->u.lab.label; if (op->type == IAOpnd_LabDiff) return op->u.labdiff.label1; } return NULL; } CLabel *InlineAsm_GetReferencedLabel2(Statement *stmt) { InlineAsm *ia = (InlineAsm *) stmt->expr; IAOperand *op; SInt32 i; for (i = 0, op = ia->args; i < ia->argcount; i++, op++) { if (op->type == IAOpnd_LabDiff) return op->u.labdiff.label2; } return NULL; } Object *InlineAsm_GetObjectOffset(InlineAsm *ia, SInt32 index, SInt32 *offset) { IAOperand *op; SInt32 i; SInt32 counter; for (i = 0, counter = 0, op = ia->args; i < ia->argcount; i++, op++) { if (op->type == IAOpnd_3) { if (counter++ == index) { *offset = ((intptr_t) &op->u.obj.obj) - ((intptr_t) ia); return op->u.obj.obj; } } } return NULL; } char *InlineAsm_DumpStatement(Statement *stmt) { static char buffer[1024]; InlineAsm *ia; IAOperand *arg; int i; char ch; SInt32 offset; ia = (InlineAsm *) stmt->expr; strcpy(buffer, "\""); strcat(buffer, InlineAsm_GetMnemonic(ia)); strcat(buffer, "\""); for (i = 0, arg = ia->args; i < ia->argcount; i++, arg++) { char argbuf[1024]; switch (arg->type) { case IAOpnd_Imm: sprintf(argbuf, " imm(%ld)", arg->u.imm.value); break; case IAOpnd_Reg: ch = ' '; if (arg->u.reg.effect & EffectWrite) { if (arg->u.reg.effect & EffectRead) ch = '+'; else ch = '='; } else { if (!(arg->u.reg.effect & EffectRead)) ch = '0'; } if (arg->u.reg.object) { sprintf(argbuf, "%creg(%s)", ch, arg->u.reg.object->name->name); } else { sprintf(argbuf, "%creg(%s%d)", ch, register_class_name[arg->u.reg.rclass], arg->u.reg.num); } break; case IAOpnd_3: case IAOpnd_4: if (arg->u.obj.offset > 0) sprintf(argbuf, " obj(%s+%ld)", arg->u.obj.obj->name->name, arg->u.obj.offset); else if (arg->u.obj.offset < 0) sprintf(argbuf, " obj(%s-%ld)", arg->u.obj.obj->name->name, -arg->u.obj.offset); else sprintf(argbuf, " obj(%s)", arg->u.obj.obj->name->name); break; case IAOpnd_Lab: sprintf(argbuf, " lab(%s)", arg->u.lab.label->uniquename->name); break; case IAOpnd_LabDiff: offset = !arg->negated ? 0 : arg->u.labdiff.offset; sprintf(argbuf, " labdiff(%s-%s%c%d)", arg->u.labdiff.label1->uniquename->name, arg->u.labdiff.label2->uniquename->name, (arg->negated == 1) ? '-' : '+', offset ); break; } strcat(buffer, argbuf); } return buffer; }