1210284Sjmallett/* 2215990Sjmallett Title: Run-time system. 3215990Sjmallett Author: Dave Matthews, Cambridge University Computer Laboratory 4210284Sjmallett 5210284Sjmallett Copyright (c) 2000 6215990Sjmallett Cambridge University Technical Services Limited 7215990Sjmallett 8215990Sjmallett Further work copyright David C. J. Matthews 2009, 2012, 2015-18 9210284Sjmallett 10215990Sjmallett This library is free software; you can redistribute it and/or 11215990Sjmallett modify it under the terms of the GNU Lesser General Public 12210284Sjmallett License version 2.1 as published by the Free Software Foundation. 13215990Sjmallett 14215990Sjmallett This library is distributed in the hope that it will be useful, 15215990Sjmallett but WITHOUT ANY WARRANTY; without even the implied warranty of 16215990Sjmallett MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17215990Sjmallett Lesser General Public License for more details. 18215990Sjmallett 19215990Sjmallett You should have received a copy of the GNU Lesser General Public 20215990Sjmallett License along with this library; if not, write to the Free Software 21215990Sjmallett Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22215990Sjmallett 23215990Sjmallett*/ 24215990Sjmallett 25215990Sjmallett#ifdef HAVE_CONFIG_H 26215990Sjmallett#include "config.h" 27215990Sjmallett#elif defined(_WIN32) 28215990Sjmallett#include "winconfig.h" 29215990Sjmallett#else 30215990Sjmallett#error "No configuration file" 31215990Sjmallett#endif 32215990Sjmallett 33215990Sjmallett#ifdef HAVE_STDIO_H 34215990Sjmallett#include <stdio.h> 35215990Sjmallett#endif 36215990Sjmallett 37215990Sjmallett#ifdef HAVE_STRING_H 38210284Sjmallett#include <string.h> 39210284Sjmallett#endif 40210284Sjmallett 41210284Sjmallett#ifdef HAVE_ASSERT_H 42210284Sjmallett#include <assert.h> 43210284Sjmallett#define ASSERT(x) assert(x) 44210284Sjmallett#else 45215990Sjmallett#define ASSERT(x) 0 46210284Sjmallett#endif 47210284Sjmallett 48210284Sjmallett#include "globals.h" 49210284Sjmallett#include "gc.h" 50210284Sjmallett#include "mpoly.h" 51215990Sjmallett#include "arb.h" 52210284Sjmallett#include "diagnostics.h" 53215990Sjmallett#include "processes.h" 54215990Sjmallett#include "profiling.h" 55215990Sjmallett#include "run_time.h" 56215990Sjmallett#include "sys.h" 57215990Sjmallett#include "polystring.h" 58215990Sjmallett#include "save_vec.h" 59215990Sjmallett#include "rtsentry.h" 60215990Sjmallett#include "memmgr.h" 61215990Sjmallett 62215990Sjmallettextern "C" { 63215990Sjmallett POLYEXTERNALSYMBOL POLYUNSIGNED PolyFullGC(FirstArgument threadId); 64215990Sjmallett POLYEXTERNALSYMBOL POLYUNSIGNED PolyIsBigEndian(); 65215990Sjmallett} 66215990Sjmallett 67215990Sjmallett#define SAVE(x) taskData->saveVec.push(x) 68210284Sjmallett#define SIZEOF(x) (sizeof(x)/sizeof(PolyWord)) 69210284Sjmallett 70210284Sjmallett 71210284Sjmallett// This is the storage allocator for allocating heap objects in the RTS. 72210284SjmallettPolyObject *alloc(TaskData *taskData, uintptr_t data_words, unsigned flags) 73210284Sjmallett/* Allocate a number of words. */ 74210284Sjmallett{ 75210284Sjmallett // Check the size. This might possibly happen with a long string. 76210284Sjmallett if (data_words > MAX_OBJECT_SIZE) 77210284Sjmallett raise_exception0(taskData, EXC_size); 78210284Sjmallett 79210284Sjmallett POLYUNSIGNED words = (POLYUNSIGNED)data_words + 1; 80215990Sjmallett 81210284Sjmallett if (profileMode == kProfileStoreAllocation) 82210284Sjmallett taskData->addProfileCount(words); 83210284Sjmallett 84215990Sjmallett PolyWord *foundSpace = processes->FindAllocationSpace(taskData, words, false); 85210284Sjmallett if (foundSpace == 0) 86210284Sjmallett { 87210284Sjmallett // Failed - the thread is set to raise an exception. 88210284Sjmallett throw IOException(); 89210284Sjmallett } 90210284Sjmallett 91210284Sjmallett PolyObject *pObj = (PolyObject*)(foundSpace + 1); 92210284Sjmallett pObj->SetLengthWord((POLYUNSIGNED)data_words, flags); 93210284Sjmallett 94210284Sjmallett // Must initialise object here, because GC doesn't clean store. 95215990Sjmallett // Is this necessary any more? This used to be necessary when we used 96210284Sjmallett // structural equality and wanted to make sure that unused bytes were cleared. 97210284Sjmallett // N.B. This sets the store to zero NOT TAGGED(0). 98210284Sjmallett for (POLYUNSIGNED i = 0; i < data_words; i++) pObj->Set(i, PolyWord::FromUnsigned(0)); 99210284Sjmallett return pObj; 100210284Sjmallett} 101210284Sjmallett 102210284SjmallettHandle alloc_and_save(TaskData *taskData, uintptr_t size, unsigned flags) 103210284Sjmallett/* Allocate and save the result on the vector. */ 104210284Sjmallett{ 105210284Sjmallett return taskData->saveVec.push(alloc(taskData, size, flags)); 106210284Sjmallett} 107210284Sjmallett 108210284SjmallettPOLYUNSIGNED PolyFullGC(FirstArgument threadId) 109210284Sjmallett{ 110210284Sjmallett TaskData *taskData = TaskData::FindTaskForId(threadId); 111210284Sjmallett ASSERT(taskData != 0); 112210284Sjmallett taskData->PreRTSCall(); 113210284Sjmallett 114210284Sjmallett try { 115210284Sjmallett // Can this raise an exception e.g. if there is insufficient memory? 116210284Sjmallett FullGC(taskData); 117210284Sjmallett } catch (...) { } // If an ML exception is raised 118215990Sjmallett 119210284Sjmallett taskData->PostRTSCall(); 120210284Sjmallett return TAGGED(0).AsUnsigned(); // Returns unit. 121210284Sjmallett} 122210284Sjmallett 123210284Sjmallett 124210284Sjmallett/******************************************************************************/ 125210284Sjmallett/* */ 126210284Sjmallett/* Error Messages */ 127210284Sjmallett/* */ 128210284Sjmallett/******************************************************************************/ 129210284Sjmallett 130210284Sjmallett 131210284Sjmallett// Return the handle to a string error message. This will return 132210284Sjmallett// something like "Unknown error" from strerror if it doesn't match 133210284Sjmallett// anything. 134210284SjmallettHandle errorMsg(TaskData *taskData, int err) 135210284Sjmallett{ 136210284Sjmallett#if (defined(_WIN32)) 137210284Sjmallett LPTSTR lpMsg = NULL; 138210284Sjmallett TCHAR *p; 139210284Sjmallett if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 140210284Sjmallett FORMAT_MESSAGE_ALLOCATE_BUFFER | 141210284Sjmallett FORMAT_MESSAGE_IGNORE_INSERTS, 142210284Sjmallett NULL, (DWORD)err, 0, (LPTSTR)&lpMsg, 1, NULL) > 0) 143210284Sjmallett { 144210284Sjmallett /* The message is returned with CRLF at the end. Remove them. */ 145210284Sjmallett for (p = lpMsg; *p != '\0' && *p != '\n' && *p != '\r'; p++); 146210284Sjmallett *p = '\0'; 147210284Sjmallett Handle res = SAVE(C_string_to_Poly(taskData, lpMsg)); 148210284Sjmallett LocalFree(lpMsg); 149210284Sjmallett return res; 150210284Sjmallett } 151210284Sjmallett#endif 152210284Sjmallett // Unix and unknown Windows errors. 153210284Sjmallett return SAVE(C_string_to_Poly(taskData, strerror(err))); 154210284Sjmallett} 155210284Sjmallett 156210284Sjmallett#define DEREFEXNHANDLE(_x) ((poly_exn *)DEREFHANDLE(_x)) 157210284Sjmallett 158210284Sjmallettstatic Handle make_exn(TaskData *taskData, int id, Handle arg, const char *fileName, int lineNo) 159210284Sjmallett{ 160210284Sjmallett const char *exName; 161210284Sjmallett switch (id) { 162210284Sjmallett case EXC_interrupt: exName = "Interrupt"; break; 163210284Sjmallett case EXC_syserr: exName = "SysErr"; break; 164210284Sjmallett case EXC_size: exName = "Size"; break; 165210284Sjmallett case EXC_overflow: exName = "Overflow"; break; 166210284Sjmallett case EXC_underflow: exName = "Underflow"; break; 167210284Sjmallett case EXC_divide: exName = "Div"; break; 168210284Sjmallett case EXC_conversion: exName = "Conversion"; break; 169210284Sjmallett case EXC_XWindows: exName = "XWindows"; break; 170210284Sjmallett case EXC_subscript: exName = "Subscript"; break; 171210284Sjmallett case EXC_foreign: exName = "Foreign"; break; 172210284Sjmallett case EXC_Fail: exName = "Fail"; break; 173210284Sjmallett case EXC_thread: exName = "Thread"; break; 174210284Sjmallett case EXC_extrace: exName = "ExTrace"; break; 175210284Sjmallett default: ASSERT(0); exName = "Unknown"; // Shouldn't happen. 176210284Sjmallett } 177210284Sjmallett 178210284Sjmallett Handle pushed_name = SAVE(C_string_to_Poly(taskData, exName)); 179210284Sjmallett 180210284Sjmallett Handle exnHandle = alloc_and_save(taskData, SIZEOF(poly_exn)); 181210284Sjmallett Handle location; 182210284Sjmallett // The location data in an exception packet is either "NoLocation" (tagged 0) 183210284Sjmallett // or the address of a record. 184210284Sjmallett if (fileName == 0) 185210284Sjmallett location = taskData->saveVec.push(TAGGED(0)); 186210284Sjmallett else 187210284Sjmallett { 188210284Sjmallett Handle file = taskData->saveVec.push(C_string_to_Poly(taskData, fileName)); 189210284Sjmallett Handle line = Make_fixed_precision(taskData, lineNo); 190210284Sjmallett location = alloc_and_save(taskData, 5); 191210284Sjmallett location->WordP()->Set(0, file->Word()); // file 192210284Sjmallett location->WordP()->Set(1, line->Word()); // startLine 193210284Sjmallett location->WordP()->Set(2, line->Word()); // endLine 194210284Sjmallett location->WordP()->Set(3, TAGGED(0)); // startPosition 195210284Sjmallett location->WordP()->Set(4, TAGGED(0)); // endPosition 196210284Sjmallett } 197210284Sjmallett 198210284Sjmallett DEREFEXNHANDLE(exnHandle)->ex_id = TAGGED(id); 199210284Sjmallett DEREFEXNHANDLE(exnHandle)->ex_name = pushed_name->Word(); 200210284Sjmallett DEREFEXNHANDLE(exnHandle)->arg = arg->Word(); 201210284Sjmallett DEREFEXNHANDLE(exnHandle)->ex_location = location->Word(); 202210284Sjmallett 203210284Sjmallett return exnHandle; 204210284Sjmallett} 205210284Sjmallett 206210284Sjmallett// Create an exception packet, e.g. Interrupt, for later use. This does not have a 207210284Sjmallett// location. 208210284Sjmallettpoly_exn *makeExceptionPacket(TaskData *taskData, int id) 209210284Sjmallett{ 210210284Sjmallett Handle exn = make_exn(taskData, id, taskData->saveVec.push(TAGGED(0)), 0, 0); 211210284Sjmallett return DEREFEXNHANDLE(exn); 212210284Sjmallett} 213210284Sjmallett 214210284Sjmallettstatic NORETURNFN(void raise_exception(TaskData *taskData, int id, Handle arg, const char *file, int line)); 215210284Sjmallett 216210284Sjmallettvoid raise_exception(TaskData *taskData, int id, Handle arg, const char *file, int line) 217210284Sjmallett/* Raise an exception with no arguments. */ 218210284Sjmallett{ 219210284Sjmallett Handle exn = make_exn(taskData, id, arg, file, line); 220210284Sjmallett taskData->SetException(DEREFEXNHANDLE(exn)); 221210284Sjmallett throw IOException(); /* Return to Poly code immediately. */ 222210284Sjmallett /*NOTREACHED*/ 223210284Sjmallett} 224210284Sjmallett 225210284Sjmallett 226215990Sjmallettvoid raiseException0WithLocation(TaskData *taskData, int id, const char *file, int line) 227210284Sjmallett/* Raise an exception with no arguments. */ 228210284Sjmallett{ 229210284Sjmallett raise_exception(taskData, id, SAVE(TAGGED(0)), file, line); 230210284Sjmallett /*NOTREACHED*/ 231210284Sjmallett} 232210284Sjmallett 233210284Sjmallettvoid raiseExceptionStringWithLocation(TaskData *taskData, int id, const char *str, const char *file, int line) 234210284Sjmallett/* Raise an exception with a C string as the argument. */ 235210284Sjmallett{ 236210284Sjmallett raise_exception(taskData, id, SAVE(C_string_to_Poly(taskData, str)), file, line); 237210284Sjmallett /*NOTREACHED*/ 238210284Sjmallett} 239210284Sjmallett 240210284Sjmallett// This is called via a macro that puts in the file name and line number. 241210284Sjmallettvoid raiseSycallWithLocation(TaskData *taskData, const char *errmsg, int err, const char *file, int line) 242210284Sjmallett{ 243210284Sjmallett if (err == 0) 244210284Sjmallett { 245210284Sjmallett Handle pushed_option = SAVE(NONE_VALUE); /* NONE */ 246210284Sjmallett Handle pushed_name = SAVE(C_string_to_Poly(taskData, errmsg)); 247210284Sjmallett Handle pair = alloc_and_save(taskData, 2); 248210284Sjmallett DEREFHANDLE(pair)->Set(0, pushed_name->Word()); 249210284Sjmallett DEREFHANDLE(pair)->Set(1, pushed_option->Word()); 250210284Sjmallett 251210284Sjmallett raise_exception(taskData, EXC_syserr, pair, file, line); 252210284Sjmallett } 253210284Sjmallett else 254210284Sjmallett { 255210284Sjmallett Handle errornum = Make_sysword(taskData, err); 256210284Sjmallett Handle pushed_option = alloc_and_save(taskData, 1); 257210284Sjmallett DEREFHANDLE(pushed_option)->Set(0, errornum->Word()); /* SOME err */ 258210284Sjmallett Handle pushed_name = errorMsg(taskData, err); // Generate the string. 259210284Sjmallett Handle pair = alloc_and_save(taskData, 2); 260210284Sjmallett DEREFHANDLE(pair)->Set(0, pushed_name->Word()); 261210284Sjmallett DEREFHANDLE(pair)->Set(1, pushed_option->Word()); 262210284Sjmallett 263210284Sjmallett raise_exception(taskData, EXC_syserr, pair, file, line); 264210284Sjmallett } 265210284Sjmallett} 266210284Sjmallett 267210284Sjmallettvoid raiseExceptionFailWithLocation(TaskData *taskData, const char *str, const char *file, int line) 268210284Sjmallett{ 269210284Sjmallett raiseExceptionStringWithLocation(taskData, EXC_Fail, str, file, line); 270210284Sjmallett} 271210284Sjmallett 272210284Sjmallett/* "Polymorphic" function to generate a list. */ 273210284SjmallettHandle makeList(TaskData *taskData, int count, char *p, int size, void *arg, 274210284Sjmallett Handle (mkEntry)(TaskData *, void*, char*)) 275210284Sjmallett{ 276210284Sjmallett Handle saved = taskData->saveVec.mark(); 277210284Sjmallett Handle list = SAVE(ListNull); 278210284Sjmallett /* Start from the end of the list. */ 279210284Sjmallett p += count*size; 280210284Sjmallett while (count > 0) 281210284Sjmallett { 282210284Sjmallett Handle value, next; 283210284Sjmallett p -= size; /* Back up to the last entry. */ 284210284Sjmallett value = mkEntry(taskData, arg, p); 285210284Sjmallett next = alloc_and_save(taskData, SIZEOF(ML_Cons_Cell)); 286210284Sjmallett 287210284Sjmallett DEREFLISTHANDLE(next)->h = value->Word(); 288210284Sjmallett DEREFLISTHANDLE(next)->t = list->Word(); 289210284Sjmallett 290210284Sjmallett taskData->saveVec.reset(saved); 291210284Sjmallett list = SAVE(next->Word()); 292210284Sjmallett count--; 293215990Sjmallett } 294215990Sjmallett return list; 295215990Sjmallett} 296215990Sjmallett 297215990Sjmallettvoid CheckAndGrowStack(TaskData *taskData, uintptr_t minSize) 298215990Sjmallett/* Expands the current stack if it has grown. We cannot shrink a stack segment 299215990Sjmallett when it grows smaller because the frame is checked only at the beginning of 300215990Sjmallett a function to ensure that there is enough space for the maximum that can 301215990Sjmallett be allocated. */ 302210284Sjmallett{ 303210284Sjmallett /* Get current size of new stack segment. */ 304215990Sjmallett uintptr_t old_len = taskData->stack->spaceSize(); 305215990Sjmallett 306215990Sjmallett if (old_len >= minSize) return; /* Ok with present size. */ 307210284Sjmallett 308210284Sjmallett // If it is too small double its size. 309210284Sjmallett uintptr_t new_len; /* New size */ 310210284Sjmallett for (new_len = old_len; new_len < minSize; new_len *= 2); 311210284Sjmallett uintptr_t limitSize = getPolyUnsigned(taskData, taskData->threadObject->mlStackSize); 312210284Sjmallett 313210284Sjmallett // Do not grow the stack if its size is already too big. 314210284Sjmallett if ((limitSize != 0 && old_len >= limitSize) || ! gMem.GrowOrShrinkStack(taskData, new_len)) 315210284Sjmallett { 316210284Sjmallett /* Cannot expand the stack any further. */ 317210284Sjmallett extern FILE *polyStderr; 318210284Sjmallett fprintf(polyStderr, "Warning - Unable to increase stack - interrupting thread\n"); 319210284Sjmallett if (debugOptions & DEBUG_THREADS) 320210284Sjmallett Log("THREAD: Unable to grow stack for thread %p from %lu to %lu\n", taskData, old_len, new_len); 321210284Sjmallett // We really should do this only if the thread is handling interrupts 322210284Sjmallett // asynchronously. On the other hand what else do we do? 323210284Sjmallett taskData->SetException(processes->GetInterrupt()); 324210284Sjmallett } 325210284Sjmallett else 326210284Sjmallett { 327210284Sjmallett if (debugOptions & DEBUG_THREADS) 328210284Sjmallett Log("THREAD: Growing stack for thread %p from %lu to %lu\n", taskData, old_len, new_len); 329210284Sjmallett } 330210284Sjmallett} 331210284Sjmallett 332210284SjmallettHandle Make_fixed_precision(TaskData *taskData, int val) 333210284Sjmallett{ 334210284Sjmallett if (val > MAXTAGGED || val < -MAXTAGGED-1) 335210284Sjmallett raise_exception0(taskData, EXC_overflow); 336210284Sjmallett return taskData->saveVec.push(TAGGED(val)); 337210284Sjmallett} 338210284Sjmallett 339210284SjmallettHandle Make_fixed_precision(TaskData *taskData, unsigned uval) 340210284Sjmallett{ 341210284Sjmallett if (uval > MAXTAGGED) 342210284Sjmallett raise_exception0(taskData, EXC_overflow); 343210284Sjmallett return taskData->saveVec.push(TAGGED(uval)); 344210284Sjmallett} 345210284Sjmallett 346210284SjmallettHandle Make_fixed_precision(TaskData *taskData, long val) 347210284Sjmallett{ 348210284Sjmallett if (val > MAXTAGGED || val < -MAXTAGGED-1) 349210284Sjmallett raise_exception0(taskData, EXC_overflow); 350210284Sjmallett return taskData->saveVec.push(TAGGED(val)); 351210284Sjmallett} 352210284Sjmallett 353210284SjmallettHandle Make_fixed_precision(TaskData *taskData, unsigned long uval) 354210284Sjmallett{ 355210284Sjmallett if (uval > MAXTAGGED) 356210284Sjmallett raise_exception0(taskData, EXC_overflow); 357210284Sjmallett return taskData->saveVec.push(TAGGED(uval)); 358210284Sjmallett} 359210284Sjmallett 360210284Sjmallett#ifdef HAVE_LONG_LONG 361210284SjmallettHandle Make_fixed_precision(TaskData *taskData, long long val) 362210284Sjmallett{ 363210284Sjmallett if (val > MAXTAGGED || val < -MAXTAGGED-1) 364210284Sjmallett raise_exception0(taskData, EXC_overflow); 365210284Sjmallett return taskData->saveVec.push(TAGGED((POLYSIGNED)val)); 366210284Sjmallett} 367210284Sjmallett 368210284SjmallettHandle Make_fixed_precision(TaskData *taskData, unsigned long long uval) 369210284Sjmallett{ 370210284Sjmallett if (uval > MAXTAGGED) 371210284Sjmallett raise_exception0(taskData, EXC_overflow); 372210284Sjmallett return taskData->saveVec.push(TAGGED((POLYUNSIGNED)uval)); 373210284Sjmallett} 374210284Sjmallett#endif 375210284Sjmallett 376210284SjmallettHandle Make_sysword(TaskData *taskData, uintptr_t p) 377210284Sjmallett{ 378210284Sjmallett Handle result = alloc_and_save(taskData, sizeof(uintptr_t)/sizeof(PolyWord), F_BYTE_OBJ); 379210284Sjmallett *(uintptr_t*)(result->Word().AsCodePtr()) = p; 380210284Sjmallett return result; 381210284Sjmallett} 382210284Sjmallett 383210284Sjmallett// A volatile ref is used for data that is not valid in a different session. 384210284Sjmallett// When loaded from a saved state it is cleared to zero. 385210284SjmallettHandle MakeVolatileWord(TaskData *taskData, void *p) 386210284Sjmallett{ 387210284Sjmallett Handle result = alloc_and_save(taskData, 388210284Sjmallett WORDS(SIZEOF_VOIDP), F_BYTE_OBJ | F_WEAK_BIT | F_MUTABLE_BIT | F_NO_OVERWRITE); 389210284Sjmallett *(void**)(result->Word().AsCodePtr()) = p; 390210284Sjmallett return result; 391210284Sjmallett} 392210284Sjmallett 393210284SjmallettHandle MakeVolatileWord(TaskData *taskData, uintptr_t p) 394210284Sjmallett{ 395210284Sjmallett return MakeVolatileWord(taskData, (void*)p); 396210284Sjmallett} 397210284Sjmallett 398210284Sjmallett// This is used to determine the endian-ness that Poly/ML is running under. 399210284Sjmallett// It's really only needed for the interpreter. In particular the pre-built 400210284Sjmallett// compiler may be running under either byte order and has to check at 401210284Sjmallett// run-time. 402210284SjmallettPOLYUNSIGNED PolyIsBigEndian() 403210284Sjmallett{ 404210284Sjmallett#ifdef WORDS_BIGENDIAN 405210284Sjmallett return TAGGED(1).AsUnsigned(); 406210284Sjmallett#else 407210284Sjmallett return TAGGED(0).AsUnsigned(); 408210284Sjmallett#endif 409210284Sjmallett} 410210284Sjmallett 411210284Sjmallettstruct _entrypts runTimeEPT[] = 412210284Sjmallett{ 413210284Sjmallett { "PolyFullGC", (polyRTSFunction)&PolyFullGC}, 414210284Sjmallett { "PolyIsBigEndian", (polyRTSFunction)&PolyIsBigEndian}, 415210284Sjmallett 416210284Sjmallett { NULL, NULL} // End of list. 417215990Sjmallett}; 418210284Sjmallett