CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { cs->pushLoc(this); defer(cs->popLoc()); auto tuple = this->right->codegen(cs).value; if(!tuple->getType()->isTupleType()) error(this->right, "Expected tuple type in assignment to tuple on left-hand-side; found type '%s' instead", tuple->getType()); auto tty = tuple->getType()->toTupleType(); std::vector<CGResult> results; size_t idx = 0; for(auto v : this->lefts) { auto res = v->codegen(cs, tty->getElementN(idx)); if(res->islvalue()) error(v, "Cannot assign to non-lvalue expression in tuple assignment"); results.push_back(res); idx++; } for(size_t i = 0; i < idx; i++) { auto lr = results[i]; auto val = cs->irb.ExtractValue(tuple, { i }); auto rr = cs->oneWayAutocast(val, lr.value->getType()); if(!rr || rr->getType() != lr.value->getType()) { error(this->right, "Mismatched types in assignment to tuple element %d; assigning type '%s' to '%s'", val->getType(), lr.value->getType()); } cs->autoAssignRefCountedValue(lr.value, rr, /* isInitial: */ false, /* performStore: */ true); } return CGResult(0); }
// walkexpr and walkstmt combined // walks the tree and adds calls to the // instrumentation code to top-level (statement) nodes' init static void racewalknode(Node **np, NodeList **init, int wr, int skip) { Node *n, *n1; NodeList *l; NodeList *fini; n = *np; if(n == N) return; if(debug['w'] > 1) dump("racewalk-before", n); setlineno(n); if(init == nil) fatal("racewalk: bad init list"); if(init == &n->ninit) { // If init == &n->ninit and n->ninit is non-nil, // racewalknode might append it to itself. // nil it out and handle it separately before putting it back. l = n->ninit; n->ninit = nil; racewalklist(l, nil); racewalknode(&n, &l, wr, skip); // recurse with nil n->ninit appendinit(&n, l); *np = n; return; } racewalklist(n->ninit, nil); switch(n->op) { default: fatal("racewalk: unknown node type %O", n->op); case OASOP: case OAS: case OAS2: case OAS2RECV: case OAS2FUNC: case OAS2MAPR: racewalknode(&n->left, init, 1, 0); racewalknode(&n->right, init, 0, 0); goto ret; case OCFUNC: case OVARKILL: // can't matter goto ret; case OBLOCK: if(n->list == nil) goto ret; switch(n->list->n->op) { case OCALLFUNC: case OCALLMETH: case OCALLINTER: // Blocks are used for multiple return function calls. // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} // We don't want to instrument between the statements because it will // smash the results. racewalknode(&n->list->n, &n->list->n->ninit, 0, 0); fini = nil; racewalklist(n->list->next, &fini); n->list = concat(n->list, fini); break; default: // Ordinary block, for loop initialization or inlined bodies. racewalklist(n->list, nil); break; } goto ret; case ODEFER: racewalknode(&n->left, init, 0, 0); goto ret; case OPROC: racewalknode(&n->left, init, 0, 0); goto ret; case OCALLINTER: racewalknode(&n->left, init, 0, 0); goto ret; case OCALLFUNC: // Instrument dst argument of runtime.writebarrier* calls // as we do not instrument runtime code. if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) { // Find the dst argument. // The list can be reordered, so it's not necessary just the first or the second element. for(l = n->list; l; l = l->next) { if(strcmp(n->left->sym->name, "writebarrierfat") == 0) { if(l->n->left->xoffset == widthptr) break; } else { if(l->n->left->xoffset == 0) break; } } if(l == nil) fatal("racewalk: writebarrier no arg"); if(l->n->right->op != OADDR) fatal("racewalk: writebarrier bad arg"); callinstr(&l->n->right->left, init, 1, 0); } racewalknode(&n->left, init, 0, 0); goto ret; case ONOT: case OMINUS: case OPLUS: case OREAL: case OIMAG: case OCOM: racewalknode(&n->left, init, wr, 0); goto ret; case ODOTINTER: racewalknode(&n->left, init, 0, 0); goto ret; case ODOT: racewalknode(&n->left, init, 0, 1); callinstr(&n, init, wr, skip); goto ret; case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND racewalknode(&n->left, init, 0, 0); callinstr(&n, init, wr, skip); goto ret; case OIND: // *p racewalknode(&n->left, init, 0, 0); callinstr(&n, init, wr, skip); goto ret; case OSPTR: case OLEN: case OCAP: racewalknode(&n->left, init, 0, 0); if(istype(n->left->type, TMAP)) { n1 = nod(OCONVNOP, n->left, N); n1->type = ptrto(types[TUINT8]); n1 = nod(OIND, n1, N); typecheck(&n1, Erv); callinstr(&n1, init, 0, skip); } goto ret; case OLSH: case ORSH: case OLROT: case OAND: case OANDNOT: case OOR: case OXOR: case OSUB: case OMUL: case OHMUL: case OEQ: case ONE: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, wr, 0); goto ret; case OANDAND: case OOROR: racewalknode(&n->left, init, wr, 0); // walk has ensured the node has moved to a location where // side effects are safe. // n->right may not be executed, // so instrumentation goes to n->right->ninit, not init. racewalknode(&n->right, &n->right->ninit, wr, 0); goto ret; case ONAME: callinstr(&n, init, wr, skip); goto ret; case OCONV: racewalknode(&n->left, init, wr, 0); goto ret; case OCONVNOP: racewalknode(&n->left, init, wr, 0); goto ret; case ODIV: case OMOD: racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, wr, 0); goto ret; case OINDEX: if(!isfixedarray(n->left->type)) racewalknode(&n->left, init, 0, 0); else if(!islvalue(n->left)) { // index of unaddressable array, like Map[k][i]. racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, 0, 0); goto ret; } racewalknode(&n->right, init, 0, 0); if(n->left->type->etype != TSTRING) callinstr(&n, init, wr, skip); goto ret; case OSLICE: case OSLICEARR: case OSLICE3: case OSLICE3ARR: // Seems to only lead to double instrumentation. //racewalknode(&n->left, init, 0, 0); goto ret; case OADDR: racewalknode(&n->left, init, 0, 1); goto ret; case OEFACE: racewalknode(&n->left, init, 0, 0); racewalknode(&n->right, init, 0, 0); goto ret; case OITAB: racewalknode(&n->left, init, 0, 0); goto ret; // should not appear in AST by now case OSEND: case ORECV: case OCLOSE: case ONEW: case OXCASE: case OXFALL: case OCASE: case OPANIC: case ORECOVER: case OCONVIFACE: case OCMPIFACE: case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: case OCALL: case OCOPY: case OAPPEND: case ORUNESTR: case OARRAYBYTESTR: case OARRAYRUNESTR: case OSTRARRAYBYTE: case OSTRARRAYRUNE: case OINDEXMAP: // lowered to call case OCMPSTR: case OADDSTR: case ODOTTYPE: case ODOTTYPE2: case OAS2DOTTYPE: case OCALLPART: // lowered to PTRLIT case OCLOSURE: // lowered to PTRLIT case ORANGE: // lowered to ordinary for loop case OARRAYLIT: // lowered to assignments case OMAPLIT: case OSTRUCTLIT: yyerror("racewalk: %O must be lowered by now", n->op); goto ret; // impossible nodes: only appear in backend. case ORROTC: case OEXTEND: yyerror("racewalk: %O cannot exist now", n->op); goto ret; // just do generic traversal case OFOR: case OIF: case OCALLMETH: case ORETURN: case ORETJMP: case OSWITCH: case OSELECT: case OEMPTY: case OBREAK: case OCONTINUE: case OFALL: case OGOTO: case OLABEL: goto ret; // does not require instrumentation case OPRINT: // don't bother instrumenting it case OPRINTN: // don't bother instrumenting it case OCHECKNIL: // always followed by a read. case OPARAM: // it appears only in fn->exit to copy heap params back case OCLOSUREVAR:// immutable pointer to captured variable case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT) case OINDREG: // at this stage, only n(SP) nodes from nodarg case ODCL: // declarations (without value) cannot be races case ODCLCONST: case ODCLTYPE: case OTYPE: case ONONAME: case OLITERAL: case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation. case OTYPESW: // ignored by code generation, do not instrument. goto ret; } ret: if(n->op != OBLOCK) // OBLOCK is handled above in a special way. racewalklist(n->list, init); if(n->ntest != N) racewalknode(&n->ntest, &n->ntest->ninit, 0, 0); if(n->nincr != N) racewalknode(&n->nincr, &n->nincr->ninit, 0, 0); racewalklist(n->nbody, nil); racewalklist(n->nelse, nil); racewalklist(n->rlist, nil); *np = n; }
CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { cs->pushLoc(this); defer(cs->popLoc()); auto lr = this->left->codegen(cs).value; auto lt = lr->getType(); if(!lr->islorclvalue()) { SpanError::make(SimpleError::make(this->loc, "Cannot assign to non-lvalue (most likely a temporary) expression")) ->add(util::ESpan(this->left->loc, "here")) ->postAndQuit(); } else if(lr->isclvalue()) { SpanError::make(SimpleError::make(this->loc, "Cannot assign to immutable expression")) ->add(util::ESpan(this->left->loc, "here")) ->postAndQuit(); } // okay, i guess auto rr = this->right->codegen(cs, lt).value; auto rt = rr->getType(); if(this->op != Operator::Assign) { // ok it's a compound assignment // auto [ newl, newr ] = cs->autoCastValueTypes(lr, rr); auto nonass = Operator::getNonAssignmentVersion(this->op); // some things -- if we're doing +=, and the types are supported, then just call the actual // append function, instead of doing the + first then assigning it. if(nonass == Operator::Plus) { if(lt->isDynamicArrayType() && lt == rt) { // right then. if(!lr->islvalue()) error(this, "Cannot append to an r-value array"); auto appendf = cgn::glue::array::getAppendFunction(cs, lt->toDynamicArrayType()); //? are there any ramifications for these actions for ref-counted things? auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, false)); cs->irb.Store(res, lr); return CGResult(0); } else if(lt->isDynamicArrayType() && lt->getArrayElementType() == rt) { // right then. if(!lr->islvalue()) error(this, "Cannot append to an r-value array"); auto appendf = cgn::glue::array::getElementAppendFunction(cs, lt->toDynamicArrayType()); //? are there any ramifications for these actions for ref-counted things? auto res = cs->irb.Call(appendf, lr, rr); cs->irb.Store(res, lr); return CGResult(0); } else if(lt->isStringType() && lt == rt) { // right then. if(!lr->islvalue()) error(this, "Cannot append to an r-value array"); auto appendf = cgn::glue::string::getAppendFunction(cs); //? are there any ramifications for these actions for ref-counted things? auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, true)); cs->irb.Store(res, lr); return CGResult(0); } else if(lt->isStringType() && rt->isCharType()) { // right then. if(!lr->islvalue()) error(this, "Cannot append to an r-value string"); auto appendf = cgn::glue::string::getCharAppendFunction(cs); //? are there any ramifications for these actions for ref-counted things? auto res = cs->irb.Call(appendf, lr, rr); cs->irb.Store(res, lr); return CGResult(0); } } // do the op first auto res = cs->performBinaryOperation(this->loc, { this->left->loc, lr }, { this->right->loc, rr }, nonass); // assign the res to the thing rr = res.value; } rr = cs->oneWayAutocast(rr, lt); if(rr == 0) { error(this, "Invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt); } // ok then if(lt != rr->getType()) error(this, "What? left = %s, right = %s", lt, rr->getType()); cs->autoAssignRefCountedValue(lr, rr, /* isInitial: */ false, /* performStore: */ true); return CGResult(0); }