Commits

Greg Bowyer  committed 8d06534

Initial attempt at pulling out "malloc" from azul MRI-J

This is the initial attempt at pulling out from the MRI-J source code the
function that would appear to be used to actually allocate (or reserve)
memory, hopefully its only a few more steps before I can alloc 100 chars,
write, free() and then have a basic example

  • Participants
  • Parent commits e9205c6

Comments (0)

Files changed (3)

File azm_mem_test/Makefile

+# Which is annoying because all of the code in this
+# "test" is actually c99
+CC=g++
+
+AZUL_FLAGS=-DAZNIX_API_VERSION=200 -DAZUL -DAZ_x86
+LIBC_POSIX_CRUFT=-D_REENTERANT \
+				 -D_XOPEN_SOURCE=600 \
+				 -D_GNU_SOURCE \
+				 -D_FILE_OFFSET_BITS=64 \
+				 -D_LARGEFILE_SOURCE
+CFLAGS=-c $(LIBC_POSIX_CRUFT) $(AZUL_FLAGS) -Wall -m64 -fPIC -fno-strict-aliasing
+INCLUDES=-I/usr/local/include
+LDFLAGS=-laznix -lpthread
+SOURCES=test.c
+OBJECTS=$(SOURCES:.c=.o)
+EXECUTABLE=azp_test
+
+all: $(SOURCES) $(EXECUTABLE)
+	
+$(EXECUTABLE): $(OBJECTS) 
+	$(CC) $(LDFLAGS) $(OBJECTS) -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $< -o $@
+
+test: all
+	azp_test
+
+clean:
+	rm -v *.o
+	rm azp_test

File azm_mem_test/test.c

+/*
+ * Copyright (c) 2012, Greg Bowyer. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * This file contains work derived from Azul and Oracle, using code liberally
+ * copy and pasted from the openJDK project and the managed runtime initiative
+ * project
+ *
+ */
 #include <stdio.h>
 #include <string.h>
 
+#include "utils.h"
+
 #include <aznix/az_memory.h>
 #include <aznix/az_pgroup.h>
 
 #include <stdlib.h>
 
 /**
- * Stupid ridiculous test to see if I can engineer
- * at least a hello-world C program that can use the azul
- * memory interfaces
+ * The following code serves as an exampler for how to use the azul
+ * MRI memory interfaces from a naive C perspective, this is done
+ * to get a handle on how these interfaces operate within hotspot
+ * in a simplified fashion
+ *
+ * From here it should be easier to see how these interfaces work
+ * within hotspot and as such translate the base ideas into pypy
  */
+
+#define GIGA(x)	(x*1024*1024*1024LL)
+#define MEGA(x)	(x*1024*1024LL)
+
+typedef void *address_t;                // User address space pointer
+
+// The following are java types nonsence {{{
+const int LogBytesPerShort   = 1;
+const int LogBytesPerInt     = 2;
+#ifdef _LP64
+const int LogBytesPerWord    = 3;
+#else
+const int LogBytesPerWord    = 2;
+#endif
+const int LogBytesPerLong    = 3;
+
+// Define various constants for virtual memory page sizes.  Having compiler
+// accessible constants is beneficial for fast code in GPGC.
+//
+// TODO - even if this is a contrieved example, we should be getting these from getpagesize()
+// I can think of at least one x86 processor where largepagesize == 4Mb not 2
+const long     LogBytesPerSmallPage = 12;  // 2^12 = 4 KB page size
+const long     LogWordsPerSmallPage = LogBytesPerSmallPage - LogBytesPerWord;
+const intptr_t BytesPerSmallPage    = 1 << LogBytesPerSmallPage;
+const intptr_t WordsPerSmallPage    = 1 << LogWordsPerSmallPage;
+
+const long     LogBytesPerLargePage = 21;  // 2^21 = 2 MB page size
+const long     LogWordsPerLargePage = LogBytesPerLargePage - LogBytesPerWord;
+const intptr_t BytesPerLargePage    = 1 << LogBytesPerLargePage;
+const intptr_t WordsPerLargePage    = 1 << LogWordsPerLargePage;
+
+// }}}
+
+enum MemoryAccount {
+    CHEAP_COMMITTED_MEMORY_ACCOUNT = AZMM_DEFAULT_ACCOUNT, // 0
+    // Rename the JHeap to make it obvious where this account belongs
+    PY_COMMITTED_MEMORY_ACCOUNT    = AZMM_JHEAP_ACCOUNT,   // 2
+    PY_PAUSE_MEMORY_ACCOUNT        = AZMM_GCPP_ACCOUNT,    // 3
+    nof_MEMORY_ACCOUNTS,
+    ALL_ACCOUNTS                   = AZMM_NR_MEMORY_ACCOUNTS
+};
+
 void os_setup_avm_launch_env() {
 
     unsigned long avm_process_id = (unsigned long) getpid();
 
     printf("Process id %d\n", avm_process_id);
 
-    int retcode = az_pmem_set_account_funds(avm_process_id, AZMM_DEFAULT_ACCOUNT,
+    // 1. Nice easy call, check is /dev/azul exists (I assume this 
+    // would also check for the proxied variety of Azul MRI)
+    int retcode = az_pmem_subsys_available();
+    assert(retcode, "The Azul subsytem was not available, no Kern module loaded ? check permissions on /dev/azul\n");
+
+    // 2. We setup ‟funds” for the default ‟account”
+    retcode = az_pmem_set_account_funds(avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT,
             AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND);
 
     if (retcode) {
         printf("FUCK! [%lu] failed to set Account [%d] with funds %d\%d: %s\n",
-                avm_process_id, AZMM_DEFAULT_ACCOUNT, AZMM_COMMITTED_FUND, 
+                avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, AZMM_COMMITTED_FUND, 
                 AZMM_OVERDRAFT_FUND, strerror(errno));
         abort();
     }
-
-    retcode = az_pmem_set_account_funds(avm_process_id, AZMM_JHEAP_ACCOUNT,
+    
+    // 3. Next setup ‟funds” for the default JVM Heap (or in our case
+    // the python / pypy objspace)
+    retcode = az_pmem_set_account_funds(avm_process_id, PY_COMMITTED_MEMORY_ACCOUNT,
                                     AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND);
     if (retcode) {
       printf("[%lu] Failed to set AC# %d with funds %d/%d: %s\n",
                avm_process_id,
-               AZMM_JHEAP_ACCOUNT, AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND,
+               PY_COMMITTED_MEMORY_ACCOUNT, AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND,
                strerror(errno));
         abort();
     }
 
-    retcode = az_pmem_set_account_funds(avm_process_id, AZMM_GCPP_ACCOUNT,
+    // 4. I *think* this is where funds are established in order to allow
+    // GPGC to operate pauseless in all but the most pathalogical of
+    // corner cases, this account is, if my reading of the code is
+    // correct, the place where the "we can collect the heap with only
+    // one free page" claim comes from
+    retcode = az_pmem_set_account_funds(avm_process_id, PY_PAUSE_MEMORY_ACCOUNT,
                                     AZMM_GCPP_FUND, AZMM_GCPP_FUND);
     if (retcode) {
       printf("[%lu] Failed to set AC# %d with funds %d/%d: %s\n",
                avm_process_id,
-               AZMM_JHEAP_ACCOUNT, AZMM_GCPP_FUND, AZMM_GCPP_FUND,
+               PY_PAUSE_MEMORY_ACCOUNT, AZMM_GCPP_FUND, AZMM_GCPP_FUND,
                strerror(errno));
         abort();
     }
     printf("[%lu] Associated accts with funds\n", avm_process_id);
 
-    retcode = az_pmem_fund_account(avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_commit);
+    // 5. Actually fund the CHeap account, we know what his overdraft limit is, now put some credit into his account
+    retcode = az_pmem_fund_account(avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, avm_mem_commit);
     if (retcode) {
       printf("[%lu] Failed to fund AC# %d with %lu: %s\n",
-               avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_commit,
+               avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, avm_mem_commit,
                strerror(errno));
         abort();
     }
     printf("[%lu] Funded AC# %d with %lu\n",
            avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_commit);
 
+    // 6. We now establish the maximum amount of memory that MRI
+    // can lay claim to, this is in essence the only real user
+    // tunable here
     retcode = az_pmem_set_maximum(avm_process_id, avm_mem_max);
     if (retcode) {
       printf("[%lu] Failed to set mem_rlimit with %lu: %s\n",
     }
     printf("[%lu] Set mem_rlimit with %lu\n", avm_process_id, avm_mem_max);
 
+    // 7. Set the maximum amount of memory that the default account can lay claim to
     retcode = az_pmem_set_account_maximum(avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_max);
     if (retcode) {
       printf("[%lu] Failed to set AC# 0 mem_rlimit with %lu: %s\n",
     printf("[%lu] Set AC# 0 mem_rlimit with %lu\n",
            avm_process_id, avm_mem_max);
 
+
     // Reserve low memory upfront for VM structures
-    int flags = AZMM_BATCHABLE;
+    //int flags = AZMM_BATCHABLE;
     /*
     size_t len = __VM_STRUCTURES_END_ADDR__ - __VM_STRUCTURES_START_ADDR__; 
     retcode = az_mreserve((address_t)__VM_STRUCTURES_START_ADDR__, len, flags);
 
 }
 
+char* azmalloc(size_t bytes, char* preferred_addr, bool must_allocate_here, 
+        bool batchable, bool aliasable) {
+	address_t addr = (address_t) preferred_addr;
+	
+	assert((size_t)addr % getpagesize() == 0, "reserve_memory on page boundaries");
+	assert(bytes % getpagesize() == 0, "reserve_memory in page-sized chunks");
+	size_t reserved_size = round_to(bytes, BytesPerLargePage);
+	assert(preferred_addr != NULL && must_allocate_here == true, 
+            "need specific addresses for reservations with azmem");
+
+	int flags = batchable ? AZMM_BATCHABLE : 0;
+	flags |= aliasable ? AZMM_ALIASABLE : 0;
+	if(flags) {
+        assert((reserved_size % (GIGA(1))) == 0, 
+                "batchable and aliasable memory needs to be in 1GB sizes" );
+    }
+
+	int ret = az_mreserve(addr, reserved_size, flags);
+	if (ret < 0) {
+		printf("osMME::reserve_memory failed: %s\n", strerror(errno));
+		addr = NULL;
+	}
+	
+	return (char*)addr;
+}
+
 int main(int argc, char** argv) {
 
     printf("First attempt to init some az mem\n");
     os_setup_avm_launch_env();
 
     return 0;
-
 }
+/* vim: set et fenc=utf-8 ff=unix sts=4 sw=4 ts=4 foldmethod=marker foldlevel=0: */

File azm_mem_test/utils.h

+/*
+ * Copyright (c) 2012, Greg Bowyer. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * This file contains functions taken directly from the OpenJDK project with a
+ * view as to porting out the Azul pauseless MRI interfaces such that they
+ * can be examined as toy programs separate to the complexities of both C4 and
+ * hotspot
+ *
+ */
+
+#ifndef __UTILS_H
+#define __UTILS_H
+
+// I suspect that stdint.h is c99, which raises questions about the
+// linking of the code for azmm, technically if I can link it under
+// g++ I *should* be good as that is both a c0x compiler (which legally
+// supersets c99) as well as a compiler that breaks the rules.
+
+#include <stdint.h>
+#include <stdio.h> // For printf
+#include <stdlib.h> // For abort
+
+// globalDefinitions.{cpp,hpp} nonsence stolen from openjdk {{{
+extern "C" void breakpoint() {
+  // use debugger to set breakpoint here
+}
+
+#define BREAKPOINT ::breakpoint() 
+// }}}
+
+#undef assert
+#define assert(cond, msg) { if (!(cond)) { fprintf(stderr, "assert fails %s %d: %s\n", __FILE__, __LINE__, msg); abort(); }}
+#define assertfalse(cond, msg) { if ((cond)) { fprintf(stderr, "assert fails %s %d: %s\n", __FILE__, __LINE__, msg); abort(); }}
+
+// }}}
+
+// intx and uintx are the 'extended' int and 'extended' unsigned int types;
+// they are 32bit wide on a 32-bit platform, and 64bit wide on a 64bit platform.
+typedef intptr_t  intx;
+typedef uintptr_t uintx;
+
+inline intptr_t round_to(intptr_t x, uintx s);
+
+const intptr_t AllBits    = ~0; // all bits set in a word
+const intptr_t NoBits     =  0; // no bits set in a word
+const int64_t  NoLongBits =  0; // no bits set in a long
+const intptr_t OneBit     =  1; // only right_most bit set in a word
+
+// bit-operations using a mask m
+inline void set_bits (intptr_t& x, intptr_t m) { x |= m; }
+inline void clear_bits (intptr_t& x, intptr_t m) { x &= ~m; }
+inline intptr_t mask_bits (intptr_t x, intptr_t m) { return x & m; }
+inline int64_t mask_long_bits (int64_t x, int64_t m) { return x & m; }
+inline bool mask_bits_are_true (intptr_t flags, intptr_t mask) { return (flags & mask) == mask; }
+
+// true if x is a power of 2, false otherwise
+inline bool is_power_of_2(intptr_t x) {
+  return ((x != NoBits) && (mask_bits(x, x - 1) == NoBits));
+}
+
+// long version of is_power_of_2
+inline bool is_power_of_2_long(int64_t x) {
+  return ((x != NoLongBits) && (mask_long_bits(x, x - 1) == NoLongBits));
+}
+
+inline intptr_t round_to(intptr_t x, uintx s) {
+    assert(!is_power_of_2(s), "s must be a power of 2");
+    const uintx m = s - 1;
+    return mask_bits(x + m, ~m);
+}
+
+#endif
+
+/* vim: set et fenc=utf-8 ff=unix sts=4 sw=4 ts=4 set foldmethod=marker foldlevel=0 : */