| 1 | /* |
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 5 | * |
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
| 7 | */ |
| 8 | |
| 9 | /* |
| 10 | * (author) Author M. Kersten |
| 11 | * For documentation see website |
| 12 | */ |
| 13 | #include "monetdb_config.h" |
| 14 | #include "mal_instruction.h" |
| 15 | #include "mal_function.h" /* for getPC() */ |
| 16 | #include "mal_utils.h" |
| 17 | #include "mal_exception.h" |
| 18 | |
| 19 | void |
| 20 | addMalException(MalBlkPtr mb, str msg) |
| 21 | { |
| 22 | str new; |
| 23 | |
| 24 | if( mb->errors){ |
| 25 | new = GDKzalloc(strlen(mb->errors) + strlen(msg) + 4); |
| 26 | if (new == NULL) |
| 27 | return ; // just stick to one error message, ignore rest |
| 28 | strcpy(new, mb->errors); |
| 29 | strcat(new, msg); |
| 30 | freeException(mb->errors); |
| 31 | mb->errors = new; |
| 32 | } else { |
| 33 | new = GDKstrdup(msg); |
| 34 | if( new == NULL) |
| 35 | return ; // just stick to one error message, ignore rest |
| 36 | mb->errors = new; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | Symbol |
| 41 | newSymbol(str nme, int kind) |
| 42 | { |
| 43 | Symbol cur; |
| 44 | |
| 45 | if (nme == NULL) |
| 46 | return NULL; |
| 47 | cur = (Symbol) GDKzalloc(sizeof(SymRecord)); |
| 48 | if (cur == NULL) |
| 49 | return NULL; |
| 50 | cur->name = putName(nme); |
| 51 | if(cur->name == NULL) { |
| 52 | GDKfree(cur); |
| 53 | return NULL; |
| 54 | } |
| 55 | cur->kind = kind; |
| 56 | cur->peer = NULL; |
| 57 | cur->def = newMalBlk(kind == FUNCTIONsymbol? STMT_INCREMENT : 2); |
| 58 | if (cur->def == NULL){ |
| 59 | GDKfree(cur); |
| 60 | return NULL; |
| 61 | } |
| 62 | return cur; |
| 63 | } |
| 64 | |
| 65 | void |
| 66 | freeSymbol(Symbol s) |
| 67 | { |
| 68 | if (s == NULL) |
| 69 | return; |
| 70 | if (s->def) { |
| 71 | freeMalBlk(s->def); |
| 72 | s->def = NULL; |
| 73 | } |
| 74 | GDKfree(s); |
| 75 | } |
| 76 | |
| 77 | void |
| 78 | freeSymbolList(Symbol s) |
| 79 | { |
| 80 | Symbol t = s; |
| 81 | |
| 82 | while (s) { |
| 83 | t = s->peer; |
| 84 | s->peer = NULL; |
| 85 | freeSymbol(s); |
| 86 | s = t; |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | int |
| 91 | newMalBlkStmt(MalBlkPtr mb, int maxstmts) |
| 92 | { |
| 93 | InstrPtr *p; |
| 94 | |
| 95 | p = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * maxstmts); |
| 96 | if (p == NULL) |
| 97 | return -1; |
| 98 | mb->stmt = p; |
| 99 | mb->stop = 0; |
| 100 | mb->ssize = maxstmts; |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | MalBlkPtr |
| 105 | newMalBlk(int elements) |
| 106 | { |
| 107 | MalBlkPtr mb; |
| 108 | VarRecord *v; |
| 109 | |
| 110 | mb = (MalBlkPtr) GDKmalloc(sizeof(MalBlkRecord)); |
| 111 | if (mb == NULL) |
| 112 | return NULL; |
| 113 | |
| 114 | /* each MAL instruction implies at least one variable |
| 115 | * we reserve some extra for constants */ |
| 116 | v = (VarRecord *) GDKzalloc(sizeof(VarRecord) * (elements + 8) ); |
| 117 | if (v == NULL) { |
| 118 | GDKfree(mb); |
| 119 | return NULL; |
| 120 | } |
| 121 | mb->var = v; |
| 122 | mb->vtop = 0; |
| 123 | mb->vid = 0; |
| 124 | mb->vsize = elements; |
| 125 | mb->help = NULL; |
| 126 | mb->binding[0] = 0; |
| 127 | mb->tag = 0; |
| 128 | mb->errors = NULL; |
| 129 | mb->alternative = NULL; |
| 130 | mb->history = NULL; |
| 131 | mb->keephistory = 0; |
| 132 | mb->maxarg = MAXARG; /* the minimum for each instruction */ |
| 133 | mb->inlineProp = 0; |
| 134 | mb->unsafeProp = 0; |
| 135 | mb->sealedProp = 0; |
| 136 | mb->replica = NULL; |
| 137 | mb->starttime = 0; |
| 138 | mb->runtime = 0; |
| 139 | mb->calls = 0; |
| 140 | mb->optimize = 0; |
| 141 | mb->stmt = NULL; |
| 142 | mb->activeClients = 1; |
| 143 | if (newMalBlkStmt(mb, elements) < 0) { |
| 144 | GDKfree(mb->var); |
| 145 | GDKfree(mb->stmt); |
| 146 | GDKfree(mb); |
| 147 | return NULL; |
| 148 | } |
| 149 | return mb; |
| 150 | } |
| 151 | |
| 152 | /* We only grow until the MAL block can be used */ |
| 153 | static int growBlk(int elm) |
| 154 | { |
| 155 | int steps =1 ; |
| 156 | int old = elm; |
| 157 | |
| 158 | while( old / 2 > 1){ |
| 159 | old /= 2; |
| 160 | steps++; |
| 161 | } |
| 162 | return elm + steps * STMT_INCREMENT; |
| 163 | } |
| 164 | |
| 165 | int |
| 166 | resizeMalBlk(MalBlkPtr mb, int elements) |
| 167 | { |
| 168 | int i; |
| 169 | |
| 170 | if( elements > mb->ssize){ |
| 171 | InstrPtr *ostmt = mb->stmt; |
| 172 | mb->stmt = (InstrPtr *) GDKrealloc(mb->stmt, elements * sizeof(InstrPtr)); |
| 173 | if ( mb->stmt ){ |
| 174 | for ( i = mb->ssize; i < elements; i++) |
| 175 | mb->stmt[i] = 0; |
| 176 | mb->ssize = elements; |
| 177 | } else { |
| 178 | mb->stmt = ostmt; /* reinstate old pointer */ |
| 179 | mb->errors = createMalException(mb,0, TYPE, "out of memory (requested: %" PRIu64" bytes)" , (uint64_t) elements * sizeof(InstrPtr)); |
| 180 | return -1; |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | |
| 185 | if( elements > mb->vsize){ |
| 186 | VarRecord *ovar = mb->var; |
| 187 | mb->var = (VarRecord*) GDKrealloc(mb->var, elements * sizeof (VarRecord)); |
| 188 | if ( mb->var ){ |
| 189 | memset( ((char*) mb->var) + sizeof(VarRecord) * mb->vsize, 0, (elements - mb->vsize) * sizeof(VarRecord)); |
| 190 | mb->vsize = elements; |
| 191 | } else{ |
| 192 | mb->var = ovar; |
| 193 | mb->errors = createMalException(mb,0, TYPE, "out of memory (requested: %" PRIu64" bytes)" , (uint64_t) elements * sizeof(InstrPtr)); |
| 194 | return -1; |
| 195 | } |
| 196 | } |
| 197 | return 0; |
| 198 | } |
| 199 | /* The resetMalBlk code removes instructions, but without freeing the |
| 200 | * space. This way the structure is prepared for re-use */ |
| 201 | void |
| 202 | resetMalBlk(MalBlkPtr mb, int stop) |
| 203 | { |
| 204 | int i; |
| 205 | |
| 206 | for(i=0; i<stop; i++) |
| 207 | mb->stmt[i] ->typechk = TYPE_UNKNOWN; |
| 208 | mb->stop = stop; |
| 209 | mb->errors = NULL; |
| 210 | } |
| 211 | |
| 212 | void |
| 213 | resetMalBlkAndFreeInstructions(MalBlkPtr mb, int stop) |
| 214 | { |
| 215 | int i; |
| 216 | |
| 217 | for(i=stop; i<mb->stop; i++) { |
| 218 | freeInstruction(mb->stmt[i]); |
| 219 | mb->stmt[i] = NULL; |
| 220 | } |
| 221 | resetMalBlk(mb, stop); |
| 222 | } |
| 223 | |
| 224 | /* The freeMalBlk code is quite defensive. It is used to localize an |
| 225 | * illegal re-use of a MAL blk. */ |
| 226 | void |
| 227 | freeMalBlk(MalBlkPtr mb) |
| 228 | { |
| 229 | int i; |
| 230 | |
| 231 | for (i = 0; i < mb->ssize; i++) |
| 232 | if (mb->stmt[i]) { |
| 233 | freeInstruction(mb->stmt[i]); |
| 234 | mb->stmt[i] = NULL; |
| 235 | } |
| 236 | mb->stop = 0; |
| 237 | for(i=0; i< mb->vtop; i++) |
| 238 | VALclear(&getVarConstant(mb,i)); |
| 239 | mb->vtop = 0; |
| 240 | mb->vid = 0; |
| 241 | GDKfree(mb->stmt); |
| 242 | mb->stmt = 0; |
| 243 | GDKfree(mb->var); |
| 244 | mb->var = 0; |
| 245 | |
| 246 | if (mb->history) |
| 247 | freeMalBlk(mb->history); |
| 248 | mb->binding[0] = 0; |
| 249 | mb->tag = 0; |
| 250 | if (mb->help) |
| 251 | GDKfree(mb->help); |
| 252 | mb->help = 0; |
| 253 | mb->inlineProp = 0; |
| 254 | mb->unsafeProp = 0; |
| 255 | mb->sealedProp = 0; |
| 256 | GDKfree(mb->errors); |
| 257 | GDKfree(mb); |
| 258 | } |
| 259 | |
| 260 | /* The routine below should assure that all referenced structures are |
| 261 | * private. The copying is memory conservative. */ |
| 262 | MalBlkPtr |
| 263 | copyMalBlk(MalBlkPtr old) |
| 264 | { |
| 265 | MalBlkPtr mb; |
| 266 | int i; |
| 267 | |
| 268 | mb = (MalBlkPtr) GDKzalloc(sizeof(MalBlkRecord)); |
| 269 | if (mb == NULL) |
| 270 | return NULL; |
| 271 | mb->alternative = old->alternative; |
| 272 | mb->history = NULL; |
| 273 | mb->keephistory = old->keephistory; |
| 274 | |
| 275 | mb->var = (VarRecord *) GDKzalloc(sizeof(VarRecord) * old->vsize); |
| 276 | if (mb->var == NULL) { |
| 277 | GDKfree(mb); |
| 278 | return NULL; |
| 279 | } |
| 280 | |
| 281 | mb->activeClients = 1; |
| 282 | mb->vsize = old->vsize; |
| 283 | mb->vtop = old->vtop; |
| 284 | mb->vid = old->vid; |
| 285 | |
| 286 | // copy all variable records |
| 287 | for (i = 0; i < old->vtop; i++) { |
| 288 | mb->var[i]= old->var[i]; |
| 289 | if (!VALcopy(&(mb->var[i].value), &(old->var[i].value))) { |
| 290 | while (--i >= 0) |
| 291 | VALclear(&mb->var[i].value); |
| 292 | GDKfree(mb->var); |
| 293 | GDKfree(mb); |
| 294 | return NULL; |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | mb->stmt = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * old->ssize); |
| 299 | |
| 300 | if (mb->stmt == NULL) { |
| 301 | for (i = 0; i < old->vtop; i++) |
| 302 | VALclear(&mb->var[i].value); |
| 303 | GDKfree(mb->var); |
| 304 | GDKfree(mb); |
| 305 | return NULL; |
| 306 | } |
| 307 | |
| 308 | mb->stop = old->stop; |
| 309 | mb->ssize = old->ssize; |
| 310 | assert(old->stop < old->ssize); |
| 311 | for (i = 0; i < old->stop; i++) { |
| 312 | mb->stmt[i] = copyInstruction(old->stmt[i]); |
| 313 | if(!mb->stmt[i]) { |
| 314 | while (--i >= 0){ |
| 315 | freeInstruction(mb->stmt[i]); |
| 316 | mb->stmt[i]= NULL; |
| 317 | } |
| 318 | for (i = 0; i < old->vtop; i++) |
| 319 | VALclear(&mb->var[i].value); |
| 320 | GDKfree(mb->var); |
| 321 | GDKfree(mb->stmt); |
| 322 | GDKfree(mb); |
| 323 | return NULL; |
| 324 | } |
| 325 | } |
| 326 | mb->help = old->help ? GDKstrdup(old->help) : NULL; |
| 327 | if (old->help && !mb->help) { |
| 328 | for (i = 0; i < old->stop; i++){ |
| 329 | freeInstruction(mb->stmt[i]); |
| 330 | mb->stmt[i]= NULL; |
| 331 | } |
| 332 | for (i = 0; i < old->vtop; i++) |
| 333 | VALclear(&mb->var[i].value); |
| 334 | GDKfree(mb->var); |
| 335 | GDKfree(mb->stmt); |
| 336 | GDKfree(mb); |
| 337 | return NULL; |
| 338 | } |
| 339 | strcpy_len(mb->binding, old->binding, sizeof(mb->binding)); |
| 340 | mb->errors = old->errors? GDKstrdup(old->errors):0; |
| 341 | mb->tag = old->tag; |
| 342 | mb->runtime = old->runtime; |
| 343 | mb->calls = old->calls; |
| 344 | mb->optimize = old->optimize; |
| 345 | mb->replica = old->replica; |
| 346 | mb->maxarg = old->maxarg; |
| 347 | mb->inlineProp = old->inlineProp; |
| 348 | mb->unsafeProp = old->unsafeProp; |
| 349 | mb->sealedProp = old->sealedProp; |
| 350 | return mb; |
| 351 | } |
| 352 | |
| 353 | void |
| 354 | addtoMalBlkHistory(MalBlkPtr mb) |
| 355 | { |
| 356 | MalBlkPtr cpy, h; |
| 357 | if (mb->keephistory) { |
| 358 | cpy = copyMalBlk(mb); |
| 359 | if (cpy == NULL) |
| 360 | return; /* ignore history */ |
| 361 | cpy->history = NULL; |
| 362 | if (mb->history == NULL) |
| 363 | mb->history = cpy; |
| 364 | else { |
| 365 | for (h = mb; h->history; h = h->history) |
| 366 | ; |
| 367 | h->history = cpy; |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | MalBlkPtr |
| 373 | getMalBlkHistory(MalBlkPtr mb, int idx) |
| 374 | { |
| 375 | MalBlkPtr h = mb; |
| 376 | |
| 377 | while (h && idx-- >= 0) |
| 378 | h = h->history; |
| 379 | return h ? h : mb; |
| 380 | } |
| 381 | |
| 382 | // Localize the plan using the optimizer name |
| 383 | MalBlkPtr |
| 384 | getMalBlkOptimized(MalBlkPtr mb, str name) |
| 385 | { |
| 386 | MalBlkPtr h = mb->history; |
| 387 | InstrPtr p; |
| 388 | int i= 0; |
| 389 | char buf[IDLENGTH]= {0}, *n; |
| 390 | size_t nlen; |
| 391 | |
| 392 | if( name == 0) |
| 393 | return mb; |
| 394 | |
| 395 | nlen = strlen(name); |
| 396 | if (nlen >= sizeof(buf)) { |
| 397 | mb->errors = createMalException(mb,0, TYPE, "Optimizer name is too large" ); |
| 398 | return NULL; |
| 399 | } |
| 400 | memcpy(buf, name, nlen + 1); |
| 401 | n = strchr(buf,']'); |
| 402 | if( n) *n = 0; |
| 403 | |
| 404 | while (h ){ |
| 405 | for( i = 1; i< h->stop; i++){ |
| 406 | p = getInstrPtr(h,i); |
| 407 | if( p->token == REMsymbol && strstr(getVarConstant(h, getArg(p,0)).val.sval, buf) ) |
| 408 | return h; |
| 409 | } |
| 410 | h = h->history; |
| 411 | } |
| 412 | return 0; |
| 413 | } |
| 414 | |
| 415 | |
| 416 | /* Before compiling a large string, it makes sense to allocate |
| 417 | * approximately enough space to keep the intermediate |
| 418 | * code. Otherwise, we end up with a repeated extend on the MAL block, |
| 419 | * which really consumes a lot of memcpy resources. The average MAL |
| 420 | * string length could been derived from the test cases. An error in |
| 421 | * the estimate is more expensive than just counting the lines. |
| 422 | */ |
| 423 | int |
| 424 | prepareMalBlk(MalBlkPtr mb, str s) |
| 425 | { |
| 426 | int cnt = STMT_INCREMENT; |
| 427 | |
| 428 | while (s) { |
| 429 | s = strchr(s, '\n'); |
| 430 | if (s) { |
| 431 | s++; |
| 432 | cnt++; |
| 433 | } |
| 434 | } |
| 435 | cnt = (int) (cnt * 1.1); |
| 436 | return resizeMalBlk(mb, cnt); |
| 437 | } |
| 438 | |
| 439 | /* The MAL records should be managed from a pool to |
| 440 | * avoid repeated alloc/free and reduce probability of |
| 441 | * memory fragmentation. (todo) |
| 442 | * The complicating factor is their variable size, |
| 443 | * which leads to growing records as a result of pushArguments |
| 444 | * Allocation of an instruction should always succeed. |
| 445 | */ |
| 446 | InstrPtr |
| 447 | newInstructionArgs(MalBlkPtr mb, str modnme, str fcnnme, int args) |
| 448 | { |
| 449 | InstrPtr p = NULL; |
| 450 | |
| 451 | p = GDKzalloc(args * sizeof(p->argv[0]) + offsetof(InstrRecord, argv)); |
| 452 | if (p == NULL) { |
| 453 | /* We are facing an hard problem. |
| 454 | * The hack is to re-use an already allocated instruction. |
| 455 | * The marking of the block as containing errors should protect further actions. |
| 456 | */ |
| 457 | if( mb){ |
| 458 | mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 459 | } |
| 460 | return NULL; |
| 461 | } |
| 462 | p->maxarg = args; |
| 463 | p->typechk = TYPE_UNKNOWN; |
| 464 | setModuleId(p, modnme); |
| 465 | setFunctionId(p, fcnnme); |
| 466 | p->argc = 1; |
| 467 | p->retc = 1; |
| 468 | p->mitosis = -1; |
| 469 | p->argv[0] = -1; /* watch out for direct use in variable table */ |
| 470 | /* Flow of control instructions are always marked as an assignment |
| 471 | * with modifier */ |
| 472 | p->token = ASSIGNsymbol; |
| 473 | return p; |
| 474 | |
| 475 | } |
| 476 | InstrPtr |
| 477 | newInstruction(MalBlkPtr mb, str modnme, str fcnnme) |
| 478 | { |
| 479 | InstrPtr p = NULL; |
| 480 | |
| 481 | p = GDKzalloc(MAXARG * sizeof(p->argv[0]) + offsetof(InstrRecord, argv)); |
| 482 | if (p == NULL) { |
| 483 | /* We are facing an hard problem. |
| 484 | * The hack is to re-use an already allocated instruction. |
| 485 | * The marking of the block as containing errors should protect further actions. |
| 486 | */ |
| 487 | if( mb){ |
| 488 | mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 489 | } |
| 490 | return NULL; |
| 491 | } |
| 492 | p->maxarg = MAXARG; |
| 493 | p->typechk = TYPE_UNKNOWN; |
| 494 | setModuleId(p, modnme); |
| 495 | setFunctionId(p, fcnnme); |
| 496 | p->argc = 1; |
| 497 | p->retc = 1; |
| 498 | p->mitosis = -1; |
| 499 | p->argv[0] = -1; /* watch out for direct use in variable table */ |
| 500 | /* Flow of control instructions are always marked as an assignment |
| 501 | * with modifier */ |
| 502 | p->token = ASSIGNsymbol; |
| 503 | return p; |
| 504 | } |
| 505 | |
| 506 | /* Copying an instruction is space conservative. */ |
| 507 | InstrPtr |
| 508 | copyInstruction(InstrPtr p) |
| 509 | { |
| 510 | InstrPtr new = (InstrPtr) GDKmalloc(offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->maxarg)); |
| 511 | if(new == NULL) |
| 512 | return new; |
| 513 | oldmoveInstruction(new, p); |
| 514 | return new; |
| 515 | } |
| 516 | |
| 517 | void |
| 518 | clrFunction(InstrPtr p) |
| 519 | { |
| 520 | p->token = ASSIGNsymbol; |
| 521 | p->fcn = 0; |
| 522 | p->blk = 0; |
| 523 | p->typechk = TYPE_UNKNOWN; |
| 524 | setModuleId(p, NULL); |
| 525 | setFunctionId(p, NULL); |
| 526 | } |
| 527 | |
| 528 | void |
| 529 | clrInstruction(InstrPtr p) |
| 530 | { |
| 531 | clrFunction(p); |
| 532 | memset((char *) p, 0, offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0])); |
| 533 | } |
| 534 | |
| 535 | void |
| 536 | freeInstruction(InstrPtr p) |
| 537 | { |
| 538 | GDKfree(p); |
| 539 | } |
| 540 | |
| 541 | /* Moving instructions around calls for care, because all dependent |
| 542 | * information should also be updated. */ |
| 543 | void |
| 544 | oldmoveInstruction(InstrPtr new, InstrPtr p) |
| 545 | { |
| 546 | int space; |
| 547 | |
| 548 | space = offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]); |
| 549 | memcpy((char *) new, (char *) p, space); |
| 550 | setFunctionId(new, getFunctionId(p)); |
| 551 | setModuleId(new, getModuleId(p)); |
| 552 | new->typechk = TYPE_UNKNOWN; |
| 553 | } |
| 554 | |
| 555 | /* Query optimizers walk their way through a MAL program block. They |
| 556 | * require some primitives to move instructions around and to remove |
| 557 | * superflous instructions. The removal is based on the assumption |
| 558 | * that indeed the instruction belonged to the block. */ |
| 559 | void |
| 560 | removeInstruction(MalBlkPtr mb, InstrPtr p) |
| 561 | { |
| 562 | int i; |
| 563 | |
| 564 | for (i = 0; i < mb->stop - 1; i++) |
| 565 | if (mb->stmt[i] == p) |
| 566 | break; |
| 567 | |
| 568 | if (i == mb->stop) |
| 569 | return; |
| 570 | |
| 571 | for (; i < mb->stop - 1; i++) |
| 572 | mb->stmt[i] = mb->stmt[i + 1]; |
| 573 | mb->stmt[i] = 0; |
| 574 | mb->stop--; |
| 575 | assert(i == mb->stop); |
| 576 | |
| 577 | /* move statement after stop */ |
| 578 | mb->stmt[i] = p; |
| 579 | } |
| 580 | |
| 581 | void |
| 582 | removeInstructionBlock(MalBlkPtr mb, int pc, int cnt) |
| 583 | { |
| 584 | int i; |
| 585 | InstrPtr p; |
| 586 | |
| 587 | for (i = pc; i < pc + cnt; i++) { |
| 588 | p = getInstrPtr(mb, i); |
| 589 | freeInstruction(p); |
| 590 | mb->stmt[i]= NULL; |
| 591 | } |
| 592 | |
| 593 | for (i = pc; i < mb->stop - cnt; i++) |
| 594 | mb->stmt[i] = mb->stmt[i + cnt]; |
| 595 | |
| 596 | mb->stop -= cnt; |
| 597 | for (; i < mb->stop; i++) |
| 598 | mb->stmt[i] = 0; |
| 599 | } |
| 600 | |
| 601 | void |
| 602 | moveInstruction(MalBlkPtr mb, int pc, int target) |
| 603 | { |
| 604 | InstrPtr p; |
| 605 | int i; |
| 606 | |
| 607 | p = getInstrPtr(mb, pc); |
| 608 | if (pc > target) { |
| 609 | for (i = pc; i > target; i--) |
| 610 | mb->stmt[i] = mb->stmt[i - 1]; |
| 611 | mb->stmt[i] = p; |
| 612 | } else { |
| 613 | for (i = target; i > pc; i--) |
| 614 | mb->stmt[i] = mb->stmt[i - 1]; |
| 615 | mb->stmt[i] = p; |
| 616 | } |
| 617 | } |
| 618 | |
| 619 | /* Beware that the first argument of a signature is reserved for the |
| 620 | * function return type , which should be equal to the destination |
| 621 | * variable type. |
| 622 | */ |
| 623 | |
| 624 | int |
| 625 | findVariable(MalBlkPtr mb, const char *name) |
| 626 | { |
| 627 | int i; |
| 628 | |
| 629 | if (name == NULL) |
| 630 | return -1; |
| 631 | for (i = mb->vtop - 1; i >= 0; i--) |
| 632 | if (idcmp(name, getVarName(mb, i)) == 0) |
| 633 | return i; |
| 634 | return -1; |
| 635 | } |
| 636 | |
| 637 | /* The second version of findVariable assumes you have not yet |
| 638 | * allocated a private structure. This is particularly useful during |
| 639 | * parsing, because most variables are already defined. This way we |
| 640 | * safe GDKmalloc/GDKfree. */ |
| 641 | int |
| 642 | findVariableLength(MalBlkPtr mb, str name, int len) |
| 643 | { |
| 644 | int i; |
| 645 | |
| 646 | for (i = mb->vtop - 1; i >= 0; i--) { |
| 647 | const char *s = getVarName(mb, i); |
| 648 | |
| 649 | if (s && strncmp(name, s, len) == 0 && s[len] == 0) |
| 650 | return i; |
| 651 | } |
| 652 | return -1; |
| 653 | } |
| 654 | |
| 655 | /* Note that getType also checks for type names directly. They have |
| 656 | * preference over variable names. */ |
| 657 | malType |
| 658 | getType(MalBlkPtr mb, str nme) |
| 659 | { |
| 660 | int i; |
| 661 | |
| 662 | i = findVariable(mb, nme); |
| 663 | if (i < 0) |
| 664 | return getAtomIndex(nme, strlen(nme), TYPE_any); |
| 665 | return getVarType(mb, i); |
| 666 | } |
| 667 | |
| 668 | str |
| 669 | getArgDefault(MalBlkPtr mb, InstrPtr p, int idx) |
| 670 | { |
| 671 | ValPtr v = &getVarConstant(mb, getArg(p, idx)); |
| 672 | |
| 673 | if (v->vtype == TYPE_str) |
| 674 | return v->val.sval; |
| 675 | return NULL; |
| 676 | } |
| 677 | |
| 678 | /* All variables are implicitly declared upon their first assignment. |
| 679 | * |
| 680 | * Lexical constants require some care. They typically appear as |
| 681 | * arguments in operator/function calls. To simplify program analysis |
| 682 | * later on, we stick to the situation that function/operator |
| 683 | * arguments are always references to by variables. |
| 684 | * |
| 685 | * Reserved words |
| 686 | * Although MAL has been designed as a minimal language, several |
| 687 | * identifiers are not eligible as variables. The encoding below is |
| 688 | * geared at simple and speed. */ |
| 689 | #if 0 |
| 690 | int |
| 691 | isReserved(str nme) |
| 692 | { |
| 693 | switch (*nme) { |
| 694 | case 'A': |
| 695 | case 'a': |
| 696 | if (idcmp("atom" , nme) == 0) |
| 697 | return 1; |
| 698 | break; |
| 699 | case 'B': |
| 700 | case 'b': |
| 701 | if (idcmp("barrier" , nme) == 0) |
| 702 | return 1; |
| 703 | break; |
| 704 | case 'C': |
| 705 | case 'c': |
| 706 | if (idcmp("command" , nme) == 0) |
| 707 | return 1; |
| 708 | break; |
| 709 | case 'E': |
| 710 | case 'e': |
| 711 | if (idcmp("exit" , nme) == 0) |
| 712 | return 1; |
| 713 | if (idcmp("end" , nme) == 0) |
| 714 | return 1; |
| 715 | break; |
| 716 | case 'F': |
| 717 | case 'f': |
| 718 | if (idcmp("false" , nme) == 0) |
| 719 | return 1; |
| 720 | if (idcmp("function" , nme) == 0) |
| 721 | return 1; |
| 722 | if (idcmp("factory" , nme) == 0) |
| 723 | return 1; |
| 724 | break; |
| 725 | case 'I': |
| 726 | case 'i': |
| 727 | if (idcmp("include" , nme) == 0) |
| 728 | return 1; |
| 729 | break; |
| 730 | case 'M': |
| 731 | case 'm': |
| 732 | if (idcmp("module" , nme) == 0) |
| 733 | return 1; |
| 734 | if (idcmp("macro" , nme) == 0) |
| 735 | return 1; |
| 736 | break; |
| 737 | case 'O': |
| 738 | case 'o': |
| 739 | if (idcmp("orcam" , nme) == 0) |
| 740 | return 1; |
| 741 | break; |
| 742 | case 'P': |
| 743 | case 'p': |
| 744 | if (idcmp("pattern" , nme) == 0) |
| 745 | return 1; |
| 746 | break; |
| 747 | case 'T': |
| 748 | case 't': |
| 749 | if (idcmp("thread" , nme) == 0) |
| 750 | return 1; |
| 751 | if (idcmp("true" , nme) == 0) |
| 752 | return 1; |
| 753 | break; |
| 754 | } |
| 755 | return 0; |
| 756 | } |
| 757 | #endif |
| 758 | |
| 759 | /* Beware, the symbol table structure assumes that it is relatively |
| 760 | * cheap to perform a linear search to a variable or constant. */ |
| 761 | static int |
| 762 | makeVarSpace(MalBlkPtr mb) |
| 763 | { |
| 764 | if (mb->vtop >= mb->vsize) { |
| 765 | VarRecord *new; |
| 766 | int s = growBlk(mb->vsize); |
| 767 | |
| 768 | new = (VarRecord*) GDKrealloc(mb->var, s * sizeof(VarRecord)); |
| 769 | if (new == NULL) { |
| 770 | // the only place to return an error signal at this stage. |
| 771 | // The Client context should be passed around more deeply |
| 772 | mb->errors = createMalException(mb,0,TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 773 | return -1; |
| 774 | } |
| 775 | memset( ((char*) new) + mb->vsize * sizeof(VarRecord), 0, (s- mb->vsize) * sizeof(VarRecord)); |
| 776 | mb->vsize = s; |
| 777 | mb->var = new; |
| 778 | } |
| 779 | return 0; |
| 780 | } |
| 781 | |
| 782 | /* create and initialize a variable record*/ |
| 783 | int |
| 784 | newVariable(MalBlkPtr mb, const char *name, size_t len, malType type) |
| 785 | { |
| 786 | int n; |
| 787 | |
| 788 | if( len >= IDLENGTH) |
| 789 | return -1; |
| 790 | if (makeVarSpace(mb)) |
| 791 | /* no space for a new variable */ |
| 792 | return -1; |
| 793 | n = mb->vtop; |
| 794 | if( name == 0 || len == 0){ |
| 795 | (void) snprintf(getVarName(mb,n), IDLENGTH,"%c%c%d" , REFMARKER, TMPMARKER,mb->vid++); |
| 796 | } else { |
| 797 | (void) strcpy_len( getVarName(mb,n), name, len + 1); |
| 798 | } |
| 799 | |
| 800 | setRowCnt(mb,n,0); |
| 801 | setVarType(mb, n, type); |
| 802 | clrVarFixed(mb, n); |
| 803 | clrVarUsed(mb, n); |
| 804 | clrVarInit(mb, n); |
| 805 | clrVarDisabled(mb, n); |
| 806 | clrVarUDFtype(mb, n); |
| 807 | clrVarConstant(mb, n); |
| 808 | clrVarCleanup(mb, n); |
| 809 | mb->vtop++; |
| 810 | return n; |
| 811 | } |
| 812 | |
| 813 | /* Simplified cloning. */ |
| 814 | int |
| 815 | cloneVariable(MalBlkPtr tm, MalBlkPtr mb, int x) |
| 816 | { |
| 817 | int res; |
| 818 | if (isVarConstant(mb, x)) |
| 819 | res = cpyConstant(tm, getVar(mb, x)); |
| 820 | else |
| 821 | res = newVariable(tm, getVarName(mb, x), strlen(getVarName(mb,x)), getVarType(mb, x)); |
| 822 | if (res < 0) |
| 823 | return res; |
| 824 | if (isVarFixed(mb, x)) |
| 825 | setVarFixed(tm, res); |
| 826 | if (isVarUsed(mb, x)) |
| 827 | setVarUsed(tm, res); |
| 828 | if (isVarInit(mb, x)) |
| 829 | setVarInit(tm, res); |
| 830 | if (isVarDisabled(mb, x)) |
| 831 | setVarDisabled(tm, res); |
| 832 | if (isVarUDFtype(mb, x)) |
| 833 | setVarUDFtype(tm, res); |
| 834 | if (isVarCleanup(mb, x)) |
| 835 | setVarCleanup(tm, res); |
| 836 | getVarSTC(tm,x) = getVarSTC(mb,x); |
| 837 | return res; |
| 838 | } |
| 839 | |
| 840 | int |
| 841 | newTmpVariable(MalBlkPtr mb, malType type) |
| 842 | { |
| 843 | return newVariable(mb,0,0,type); |
| 844 | } |
| 845 | |
| 846 | int |
| 847 | newTypeVariable(MalBlkPtr mb, malType type) |
| 848 | { |
| 849 | int n, i; |
| 850 | for (i = 0; i < mb->vtop; i++) |
| 851 | if (isVarTypedef(mb, i) && getVarType(mb, i) == type) |
| 852 | break; |
| 853 | |
| 854 | if( i < mb->vtop ) |
| 855 | return i; |
| 856 | n = newTmpVariable(mb, type); |
| 857 | setVarTypedef(mb, n); |
| 858 | return n; |
| 859 | } |
| 860 | |
| 861 | void |
| 862 | clearVariable(MalBlkPtr mb, int varid) |
| 863 | { |
| 864 | VarPtr v; |
| 865 | |
| 866 | v = getVar(mb, varid); |
| 867 | if (isVarConstant(mb, varid) || isVarDisabled(mb, varid)) |
| 868 | VALclear(&v->value); |
| 869 | v->type = 0; |
| 870 | v->constant= 0; |
| 871 | v->typevar= 0; |
| 872 | v->fixedtype= 0; |
| 873 | v->udftype= 0; |
| 874 | v->cleanup= 0; |
| 875 | v->initialized= 0; |
| 876 | v->used= 0; |
| 877 | v->rowcnt = 0; |
| 878 | v->eolife = 0; |
| 879 | v->stc = 0; |
| 880 | } |
| 881 | |
| 882 | void |
| 883 | freeVariable(MalBlkPtr mb, int varid) |
| 884 | { |
| 885 | clearVariable(mb, varid); |
| 886 | } |
| 887 | |
| 888 | /* A special action is to reduce the variable space by removing all |
| 889 | * that do not contribute. |
| 890 | * All temporary variables are renamed in the process to trim the varid. |
| 891 | */ |
| 892 | void |
| 893 | trimMalVariables_(MalBlkPtr mb, MalStkPtr glb) |
| 894 | { |
| 895 | int *alias, cnt = 0, i, j; |
| 896 | InstrPtr q; |
| 897 | |
| 898 | if( mb->vtop == 0) |
| 899 | return; |
| 900 | alias = (int *) GDKzalloc(mb->vtop * sizeof(int)); |
| 901 | if (alias == NULL) |
| 902 | return; /* forget it if we run out of memory */ |
| 903 | |
| 904 | /* build the alias table */ |
| 905 | for (i = 0; i < mb->vtop; i++) { |
| 906 | #ifdef DEBUG_REDUCE |
| 907 | fprintf(stderr,"used %s %d\n" , getVarName(mb,i), isVarUsed(mb,i)); |
| 908 | #endif |
| 909 | if ( isVarUsed(mb,i) == 0) { |
| 910 | if (glb && isVarConstant(mb, i)) |
| 911 | VALclear(&glb->stk[i]); |
| 912 | freeVariable(mb, i); |
| 913 | continue; |
| 914 | } |
| 915 | if (i > cnt) { |
| 916 | /* remap temporary variables */ |
| 917 | VarRecord t = mb->var[cnt]; |
| 918 | mb->var[cnt] = mb->var[i]; |
| 919 | mb->var[i] = t; |
| 920 | } |
| 921 | |
| 922 | /* valgrind finds a leak when we move these variable record |
| 923 | * pointers around. */ |
| 924 | alias[i] = cnt; |
| 925 | if (glb && i != cnt) { |
| 926 | glb->stk[cnt] = glb->stk[i]; |
| 927 | VALempty(&glb->stk[i]); |
| 928 | } |
| 929 | cnt++; |
| 930 | } |
| 931 | #ifdef DEBUG_REDUCE |
| 932 | fprintf(stderr, "Variable reduction %d -> %d\n" , mb->vtop, cnt); |
| 933 | for (i = 0; i < mb->vtop; i++) |
| 934 | fprintf(stderr, "map %d->%d\n" , i, alias[i]); |
| 935 | #endif |
| 936 | |
| 937 | /* remap all variable references to their new position. */ |
| 938 | if (cnt < mb->vtop) { |
| 939 | for (i = 0; i < mb->stop; i++) { |
| 940 | q = getInstrPtr(mb, i); |
| 941 | for (j = 0; j < q->argc; j++){ |
| 942 | #ifdef DEBUG_REDUCE |
| 943 | fprintf(stderr, "map %d->%d\n" , getArg(q,j), alias[getArg(q,j)]); |
| 944 | #endif |
| 945 | getArg(q, j) = alias[getArg(q, j)]; |
| 946 | } |
| 947 | } |
| 948 | } |
| 949 | /* rename the temporary variable */ |
| 950 | mb->vid = 0; |
| 951 | for( i =0; i< cnt; i++) |
| 952 | if( isTmpVar(mb,i)) |
| 953 | (void) snprintf(mb->var[i].id, IDLENGTH,"%c%c%d" , REFMARKER, TMPMARKER,mb->vid++); |
| 954 | |
| 955 | #ifdef DEBUG_REDUCE |
| 956 | fprintf(stderr, "After reduction \n" ); |
| 957 | fprintFunction(stderr, mb, 0, 0); |
| 958 | #endif |
| 959 | GDKfree(alias); |
| 960 | mb->vtop = cnt; |
| 961 | } |
| 962 | |
| 963 | void |
| 964 | trimMalVariables(MalBlkPtr mb, MalStkPtr stk) |
| 965 | { |
| 966 | int i, j; |
| 967 | InstrPtr q; |
| 968 | |
| 969 | /* reset the use bit for all non-signature arguments */ |
| 970 | for (i = 0; i < mb->vtop; i++) |
| 971 | clrVarUsed(mb,i); |
| 972 | /* build the use table */ |
| 973 | for (i = 0; i < mb->stop; i++) { |
| 974 | q = getInstrPtr(mb, i); |
| 975 | |
| 976 | for (j = 0; j < q->argc; j++) |
| 977 | setVarUsed(mb,getArg(q,j)); |
| 978 | } |
| 979 | trimMalVariables_(mb, stk); |
| 980 | } |
| 981 | |
| 982 | /* MAL constants |
| 983 | * Constants are stored in the symbol table and referenced by a |
| 984 | * variable identifier. This means that per MAL instruction, we may |
| 985 | * end up with MAXARG entries in the symbol table. This may lead to |
| 986 | * long searches for variables. An optimization strategy deployed in |
| 987 | * the current implementation is to look around for a similar |
| 988 | * (constant) definition and to reuse its identifier. This avoids an |
| 989 | * exploding symbol table with a lot of temporary variables (as in |
| 990 | * tst400cHuge) |
| 991 | * |
| 992 | * But then the question becomes how far to search? Searching through |
| 993 | * all variables is only useful when the list remains short or when |
| 994 | * the constant-variable-name is easily derivable from its literal |
| 995 | * value and a hash-based index leads you quickly to it. |
| 996 | * |
| 997 | * For the time being, we use a MAL system parameter, MAL_VAR_WINDOW, |
| 998 | * to indicate the number of symbol table entries to consider. Setting |
| 999 | * it to >= MAXARG will at least capture repeated use of a constant |
| 1000 | * within a single function call or repeated use within a small block |
| 1001 | * of code. |
| 1002 | * |
| 1003 | * The final step is to prepare a GDK value record, from which the |
| 1004 | * internal representation can be obtained during MAL interpretation. |
| 1005 | * |
| 1006 | * The constant values are linked together to improve searching |
| 1007 | * them. This start of the constant list is kept in the MalBlk. |
| 1008 | * |
| 1009 | * Conversion of a constant to another type is limited to well-known |
| 1010 | * coercion rules. Errors are reported and the nil value is set. */ |
| 1011 | |
| 1012 | /* Converts the constant in vr to the MAL type type. Conversion is |
| 1013 | * done in the vr struct. */ |
| 1014 | str |
| 1015 | convertConstant(int type, ValPtr vr) |
| 1016 | { |
| 1017 | if( type > GDKatomcnt ) |
| 1018 | throw(SYNTAX, "convertConstant" , "type index out of bound" ); |
| 1019 | if (vr->vtype == type) |
| 1020 | return MAL_SUCCEED; |
| 1021 | if (type == TYPE_bat || isaBatType(type)) { |
| 1022 | /* BAT variables can only be set to nil */ |
| 1023 | VALclear(vr); |
| 1024 | vr->vtype = type; |
| 1025 | vr->val.bval = bat_nil; |
| 1026 | return MAL_SUCCEED; |
| 1027 | } |
| 1028 | if (type == TYPE_ptr) { |
| 1029 | /* all coercions should be avoided to protect against memory probing */ |
| 1030 | if (vr->vtype == TYPE_void) { |
| 1031 | VALclear(vr); |
| 1032 | vr->vtype = type; |
| 1033 | vr->val.pval = 0; |
| 1034 | return MAL_SUCCEED; |
| 1035 | } |
| 1036 | if (vr->vtype != type) |
| 1037 | throw(SYNTAX, "convertConstant" , "pointer conversion error" ); |
| 1038 | return MAL_SUCCEED; |
| 1039 | } |
| 1040 | if (type == TYPE_any) { |
| 1041 | #ifndef DEBUG_MAL_INSTR |
| 1042 | assert(0); |
| 1043 | #endif |
| 1044 | throw(SYNTAX, "convertConstant" , "missing type" ); |
| 1045 | } |
| 1046 | if (VALconvert(type, vr) == NULL) { |
| 1047 | if (vr->vtype == TYPE_str) |
| 1048 | throw(SYNTAX, "convertConstant" , "parse error in '%s'" , vr->val.sval); |
| 1049 | throw(SYNTAX, "convertConstant" , "coercion failed" ); |
| 1050 | } |
| 1051 | return MAL_SUCCEED; |
| 1052 | } |
| 1053 | |
| 1054 | int |
| 1055 | fndConstant(MalBlkPtr mb, const ValRecord *cst, int depth) |
| 1056 | { |
| 1057 | int i, k; |
| 1058 | const void *p; |
| 1059 | |
| 1060 | /* pointers never match */ |
| 1061 | if (ATOMstorage(cst->vtype) == TYPE_ptr) |
| 1062 | return -1; |
| 1063 | |
| 1064 | p = VALptr(cst); |
| 1065 | k = mb->vtop - depth; |
| 1066 | if (k < 0) |
| 1067 | k = 0; |
| 1068 | for (i=k; i < mb->vtop - 1; i++) |
| 1069 | if (getVar(mb,i) && isVarConstant(mb,i)){ |
| 1070 | VarPtr v = getVar(mb, i); |
| 1071 | if (v && v->type == cst->vtype && ATOMcmp(cst->vtype, VALptr(&v->value), p) == 0) |
| 1072 | return i; |
| 1073 | } |
| 1074 | return -1; |
| 1075 | } |
| 1076 | |
| 1077 | int |
| 1078 | cpyConstant(MalBlkPtr mb, VarPtr vr) |
| 1079 | { |
| 1080 | int i; |
| 1081 | ValRecord cst; |
| 1082 | |
| 1083 | if (VALcopy(&cst, &vr->value) == NULL) |
| 1084 | return -1; |
| 1085 | |
| 1086 | i = defConstant(mb, vr->type, &cst); |
| 1087 | return i; |
| 1088 | } |
| 1089 | |
| 1090 | int |
| 1091 | defConstant(MalBlkPtr mb, int type, ValPtr cst) |
| 1092 | { |
| 1093 | int k; |
| 1094 | str msg; |
| 1095 | |
| 1096 | if (isaBatType(type) && cst->vtype == TYPE_void) { |
| 1097 | cst->vtype = TYPE_bat; |
| 1098 | cst->val.bval = bat_nil; |
| 1099 | } else if (cst->vtype != type && !isaBatType(type) && !isPolyType(type)) { |
| 1100 | int otype = cst->vtype; |
| 1101 | assert(type != TYPE_any); /* help Coverity */ |
| 1102 | msg = convertConstant(type, cst); |
| 1103 | if (msg) { |
| 1104 | str ft, tt; |
| 1105 | |
| 1106 | /* free old value */ |
| 1107 | ft = getTypeName(otype); |
| 1108 | tt = getTypeName(type); |
| 1109 | mb->errors = createMalException(mb, 0, TYPE, "constant coercion error from %s to %s" , ft, tt); |
| 1110 | GDKfree(ft); |
| 1111 | GDKfree(tt); |
| 1112 | freeException(msg); |
| 1113 | } else { |
| 1114 | assert(cst->vtype == type); |
| 1115 | } |
| 1116 | } |
| 1117 | k = fndConstant(mb, cst, MAL_VAR_WINDOW); |
| 1118 | if (k >= 0) { |
| 1119 | /* protect against leaks coming from constant reuse */ |
| 1120 | if (ATOMextern(type) && cst->val.pval) |
| 1121 | VALclear(cst); |
| 1122 | return k; |
| 1123 | } |
| 1124 | k = newTmpVariable(mb, type); |
| 1125 | setVarConstant(mb, k); |
| 1126 | setVarFixed(mb, k); |
| 1127 | if (type >= 0 && type < GDKatomcnt && ATOMextern(type)) |
| 1128 | setVarCleanup(mb, k); |
| 1129 | else |
| 1130 | clrVarCleanup(mb, k); |
| 1131 | if(VALcopy( &getVarConstant(mb, k),cst) == NULL) |
| 1132 | return -1; |
| 1133 | if (ATOMextern(cst->vtype) && cst->val.pval) |
| 1134 | VALclear(cst); |
| 1135 | return k; |
| 1136 | } |
| 1137 | |
| 1138 | /* Argument handling |
| 1139 | * The number of arguments for procedures is currently |
| 1140 | * limited. Furthermore, we should assure that no variable is |
| 1141 | * referenced before being assigned. Failure to obey should mark the |
| 1142 | * instruction as type-error. */ |
| 1143 | InstrPtr |
| 1144 | pushArgument(MalBlkPtr mb, InstrPtr p, int varid) |
| 1145 | { |
| 1146 | if (p == NULL) |
| 1147 | return NULL; |
| 1148 | if (varid < 0) { |
| 1149 | /* leave everything as is in this exceptional programming error */ |
| 1150 | mb->errors = createMalException(mb, 0, TYPE,"improper variable id" ); |
| 1151 | return p; |
| 1152 | } |
| 1153 | |
| 1154 | if (p->argc + 1 == p->maxarg) { |
| 1155 | int i = 0; |
| 1156 | int space = p->maxarg * sizeof(p->argv[0]) + offsetof(InstrRecord, argv); |
| 1157 | InstrPtr pn = (InstrPtr) GDKrealloc(p,space + MAXARG * sizeof(p->argv[0])); |
| 1158 | |
| 1159 | if (pn == NULL) { |
| 1160 | /* In the exceptional case we can not allocate more space |
| 1161 | * then we show an exception, mark the block as erroneous |
| 1162 | * and leave the instruction as is. |
| 1163 | */ |
| 1164 | mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 1165 | return p; |
| 1166 | } |
| 1167 | memset( ((char*)pn) + space, 0, MAXARG * sizeof(pn->argv[0])); |
| 1168 | pn->maxarg += MAXARG; |
| 1169 | |
| 1170 | /* if the instruction is already stored in the MAL block |
| 1171 | * it should be replaced by an extended version. |
| 1172 | */ |
| 1173 | if( p != pn) |
| 1174 | for (i = mb->stop - 1; i >= 0; i--) |
| 1175 | if (mb->stmt[i] == p) { |
| 1176 | mb->stmt[i] = pn; |
| 1177 | break; |
| 1178 | } |
| 1179 | |
| 1180 | p = pn; |
| 1181 | /* we have to keep track on the maximal arguments/block |
| 1182 | * because it is needed by the interpreter */ |
| 1183 | if (mb->maxarg < pn->maxarg) |
| 1184 | mb->maxarg = pn->maxarg; |
| 1185 | } |
| 1186 | /* protect against the case that the instruction is malloced |
| 1187 | * in isolation */ |
| 1188 | if( mb->maxarg < p->maxarg) |
| 1189 | mb->maxarg= p->maxarg; |
| 1190 | |
| 1191 | p->argv[p->argc++] = varid; |
| 1192 | return p; |
| 1193 | } |
| 1194 | |
| 1195 | InstrPtr |
| 1196 | setArgument(MalBlkPtr mb, InstrPtr p, int idx, int varid) |
| 1197 | { |
| 1198 | int i; |
| 1199 | |
| 1200 | if (p == NULL) |
| 1201 | return NULL; |
| 1202 | p = pushArgument(mb, p, varid); /* make space */ |
| 1203 | if (p == NULL) |
| 1204 | return NULL; |
| 1205 | for (i = p->argc - 1; i > idx; i--) |
| 1206 | getArg(p, i) = getArg(p, i - 1); |
| 1207 | getArg(p, i) = varid; |
| 1208 | return p; |
| 1209 | } |
| 1210 | |
| 1211 | InstrPtr |
| 1212 | pushReturn(MalBlkPtr mb, InstrPtr p, int varid) |
| 1213 | { |
| 1214 | if (p->retc == 1 && p->argv[0] == -1) { |
| 1215 | p->argv[0] = varid; |
| 1216 | return p; |
| 1217 | } |
| 1218 | if ((p = setArgument(mb, p, p->retc, varid)) == NULL) |
| 1219 | return NULL; |
| 1220 | p->retc++; |
| 1221 | return p; |
| 1222 | } |
| 1223 | |
| 1224 | /* Store the information of a destination variable in the signature |
| 1225 | * structure of each instruction. This code is largely equivalent to |
| 1226 | * pushArgument, but it is more efficient in searching and collecting |
| 1227 | * the information. |
| 1228 | * TODO */ |
| 1229 | /* swallows name argument */ |
| 1230 | InstrPtr |
| 1231 | pushArgumentId(MalBlkPtr mb, InstrPtr p, const char *name) |
| 1232 | { |
| 1233 | int v; |
| 1234 | |
| 1235 | if (p == NULL) |
| 1236 | return NULL; |
| 1237 | v = findVariable(mb, name); |
| 1238 | if (v < 0) { |
| 1239 | size_t namelen = strlen(name); |
| 1240 | if ((v = newVariable(mb, name, namelen, getAtomIndex(name, namelen, TYPE_any))) < 0) { |
| 1241 | freeInstruction(p); |
| 1242 | return NULL; |
| 1243 | } |
| 1244 | } |
| 1245 | return pushArgument(mb, p, v); |
| 1246 | } |
| 1247 | |
| 1248 | /* The alternative is to remove arguments from an instruction |
| 1249 | * record. This is typically part of instruction constructions. */ |
| 1250 | void |
| 1251 | delArgument(InstrPtr p, int idx) |
| 1252 | { |
| 1253 | int i; |
| 1254 | |
| 1255 | for (i = idx; i < p->argc - 1; i++) |
| 1256 | p->argv[i] = p->argv[i + 1]; |
| 1257 | p->argc--; |
| 1258 | if (idx < p->retc) |
| 1259 | p->retc--; |
| 1260 | } |
| 1261 | |
| 1262 | void |
| 1263 | setArgType(MalBlkPtr mb, InstrPtr p, int i, int tpe) |
| 1264 | { |
| 1265 | assert(p->argv[i] < mb->vsize); |
| 1266 | setVarType(mb,getArg(p, i),tpe); |
| 1267 | } |
| 1268 | |
| 1269 | void |
| 1270 | setReturnArgument(InstrPtr p, int i) |
| 1271 | { |
| 1272 | setDestVar(p, i); |
| 1273 | } |
| 1274 | |
| 1275 | malType |
| 1276 | destinationType(MalBlkPtr mb, InstrPtr p) |
| 1277 | { |
| 1278 | if (p->argc > 0) |
| 1279 | return getVarType(mb, getDestVar(p)); |
| 1280 | return TYPE_any; |
| 1281 | } |
| 1282 | |
| 1283 | /* For polymorphic instructions we should keep around the maximal |
| 1284 | * index to later allocate sufficient space for type resolutions maps. |
| 1285 | * Beware, that we should only consider the instruction polymorphic if |
| 1286 | * it has a positive index or belongs to the signature. |
| 1287 | * BATs can only have a polymorphic type at the tail. |
| 1288 | */ |
| 1289 | inline void |
| 1290 | setPolymorphic(InstrPtr p, int tpe, int force) |
| 1291 | { |
| 1292 | int c1 = 0, c2 = 0; |
| 1293 | |
| 1294 | if (force == FALSE && tpe == TYPE_any) |
| 1295 | return; |
| 1296 | if (isaBatType(tpe)) |
| 1297 | c1= TYPE_oid; |
| 1298 | if (getTypeIndex(tpe) > 0) |
| 1299 | c2 = getTypeIndex(tpe); |
| 1300 | else if (getBatType(tpe) == TYPE_any) |
| 1301 | c2 = 1; |
| 1302 | c1 = c1 > c2 ? c1 : c2; |
| 1303 | if (c1 > 0 && c1 >= p->polymorphic) |
| 1304 | p->polymorphic = c1 + 1; |
| 1305 | } |
| 1306 | |
| 1307 | /* Instructions are simply appended to a MAL block. It should always succeed. |
| 1308 | * The assumption is to push it when you are completely done with its preparation. |
| 1309 | */ |
| 1310 | |
| 1311 | void |
| 1312 | pushInstruction(MalBlkPtr mb, InstrPtr p) |
| 1313 | { |
| 1314 | int i; |
| 1315 | int ; |
| 1316 | InstrPtr q; |
| 1317 | |
| 1318 | if (p == NULL) |
| 1319 | return; |
| 1320 | |
| 1321 | extra = mb->vsize - mb->vtop; // the extra variables already known |
| 1322 | if (mb->stop + 1 >= mb->ssize) { |
| 1323 | if( resizeMalBlk(mb, growBlk(mb->ssize) + extra) ){ |
| 1324 | /* perhaps we can continue with a smaller increment. |
| 1325 | * But the block remains marked as faulty. |
| 1326 | */ |
| 1327 | if( resizeMalBlk(mb,mb->ssize + 1)){ |
| 1328 | /* we are now left with the situation that the new instruction is dangling . |
| 1329 | * The hack is to take an instruction out of the block that is likely not referenced independently |
| 1330 | * The last resort is to take the first, which should always be there |
| 1331 | * This assumes that no references are kept elsewhere to the statement |
| 1332 | */ |
| 1333 | for( i = 1; i < mb->stop; i++){ |
| 1334 | q= getInstrPtr(mb,i); |
| 1335 | if( q->token == REMsymbol){ |
| 1336 | freeInstruction(q); |
| 1337 | mb->stmt[i] = p; |
| 1338 | return; |
| 1339 | } |
| 1340 | } |
| 1341 | freeInstruction(getInstrPtr(mb,0)); |
| 1342 | mb->stmt[0] = p; |
| 1343 | return; |
| 1344 | } |
| 1345 | } |
| 1346 | } |
| 1347 | if (mb->stmt[mb->stop]) |
| 1348 | freeInstruction(mb->stmt[mb->stop]); |
| 1349 | mb->stmt[mb->stop++] = p; |
| 1350 | } |
| 1351 | |