MWCC/compiler_and_linker/BackEnd/PowerPC/InlineAssembler/InlineAsm.c

681 lines
20 KiB
C

#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_INLINE_DATA))
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;
}