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