jit: port libgccjit to Windows
authorNicolás Bértolo <nicolasbertolo@gmail.com>
Fri, 22 May 2020 20:54:41 +0000 (17:54 -0300)
committerDavid Malcolm <dmalcolm@redhat.com>
Thu, 28 May 2020 18:43:58 +0000 (14:43 -0400)
2020-05-28  Nicolas Bértolo  <nicolasbertolo@gmail.com>

/ChangeLog:
* configure.ac: Don't require --enable-host-shared when building
for Mingw.
* configure: Regenerate.

2020-05-28  Nicolas Bértolo  <nicolasbertolo@gmail.com>

gcc/ChangeLog:
* Makefile.in: don't look for libiberty in the "pic" subdirectory
when building for Mingw. Add dependency on xgcc with the proper
extension.

2020-05-28  Nicolas Bértolo  <nicolasbertolo@gmail.com>

gcc/c/ChangeLog:
* Make-lang.in: Remove extra slash.

2020-05-28  Nicolas Bértolo  <nicolasbertolo@gmail.com>

gcc/jit/ChangeLog:
* Make-lang.in: Remove extra slash. Build libgccjit.dll and its
import library in Windows.
* config-lang.in: Update comment about --enable-host-shared.
* jit-w32.h: New file.
* jit-w32.c: New file.
(print_last_error): New function that prints the error
string corresponding to GetLastError().
(get_TOKEN_USER_current_user): Helper function used for getting
the SID belonging to the current user.
(create_directory_for_current_user): Helper function to create
a directory with permissions such that only the current user can
access it.
(win_mkdtemp): Create a temporary directory using Windows APIs.
* jit-playback.c: Do not chmod files in Windows. Use LoadLibrary,
FreeLibrary and GetProcAddress instead of libdl.
* jit-result.h, jit-result.c: Introduce result::handle_t to
abstract over the types used for dynamic library handles.
* jit-tempdir.c: Do not use mkdtemp() in Windows, use
win_mkdtemp().

12 files changed:
configure
configure.ac
gcc/Makefile.in
gcc/c/Make-lang.in
gcc/jit/Make-lang.in
gcc/jit/config-lang.in
gcc/jit/jit-playback.c
gcc/jit/jit-result.c
gcc/jit/jit-result.h
gcc/jit/jit-tempdir.c
gcc/jit/jit-w32.c [new file with mode: 0644]
gcc/jit/jit-w32.h [new file with mode: 0644]

index 3af6a530b9adea965bfbb07cb19c28574a51d977..b7897446c7007cee025de21fb77f9755c34db96d 100755 (executable)
--- a/configure
+++ b/configure
@@ -6489,9 +6489,13 @@ $as_echo "$as_me: WARNING: GNAT is required to build $language" >&2;}
         esac
 
         # Disable jit if -enable-host-shared not specified
-        case ${add_this_lang}:${language}:${host_shared} in
-          yes:jit:no)
-           # PR jit/64780: explicitly specify --enable-host-shared
+        # but not if building for Mingw
+        case $target in
+          *mingw*) ;;
+          *)
+          case ${add_this_lang}:${language}:${host_shared} in
+            yes:jit:no)
+                  # PR jit/64780: explicitly specify --enable-host-shared
            as_fn_error $? "
 Enabling language \"jit\" requires --enable-host-shared.
 
@@ -6502,17 +6506,19 @@ If you want to build both the jit and the regular compiler, it is often
 best to do this via two separate configure/builds, in separate
 directories, to avoid imposing the performance cost of
 --enable-host-shared on the regular compiler." "$LINENO" 5
-           ;;
-          all:jit:no)
-           { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --enable-host-shared required to build $language" >&5
+                   ;;
+            all:jit:no)
+             { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --enable-host-shared required to build $language" >&5
 $as_echo "$as_me: WARNING: --enable-host-shared required to build $language" >&2;}
-            add_this_lang=unsupported
-            ;;
-          *:jit:no)
-            # Silently disable.
-            add_this_lang=unsupported
-            ;;
-       esac
+              add_this_lang=unsupported
+              ;;
+            *:jit:no)
+              # Silently disable.
+              add_this_lang=unsupported
+              ;;
+               esac
+          ;;
+        esac
 
         # Disable a language that is unsupported by the target.
        case "${add_this_lang}: $unsupported_languages " in
index a67801371a4eabd38e5d99398e17134e5166447c..59bd92a3e536588c18e6b1b0e8d8aac3c6c44cef 100644 (file)
@@ -2079,9 +2079,14 @@ if test -d ${srcdir}/gcc; then
         esac
 
         # Disable jit if -enable-host-shared not specified
-        case ${add_this_lang}:${language}:${host_shared} in
-          yes:jit:no)
-           # PR jit/64780: explicitly specify --enable-host-shared
+        # but not if building for Mingw. All code in Windows
+        # is position independent code (PIC).
+        case $target in
+          *mingw*) ;;
+          *)
+          case ${add_this_lang}:${language}:${host_shared} in
+            yes:jit:no)
+                  # PR jit/64780: explicitly specify --enable-host-shared
            AC_MSG_ERROR([
 Enabling language "jit" requires --enable-host-shared.
 
@@ -2092,16 +2097,18 @@ If you want to build both the jit and the regular compiler, it is often
 best to do this via two separate configure/builds, in separate
 directories, to avoid imposing the performance cost of
 --enable-host-shared on the regular compiler.])
-           ;;
-          all:jit:no)
-           AC_MSG_WARN([--enable-host-shared required to build $language])
-            add_this_lang=unsupported
-            ;;
-          *:jit:no)
-            # Silently disable.
-            add_this_lang=unsupported
-            ;;
-       esac
+                   ;;
+            all:jit:no)
+             AC_MSG_WARN([--enable-host-shared required to build $language])
+              add_this_lang=unsupported
+              ;;
+            *:jit:no)
+              # Silently disable.
+              add_this_lang=unsupported
+              ;;
+               esac
+          ;;
+        esac
 
         # Disable a language that is unsupported by the target.
        case "${add_this_lang}: $unsupported_languages " in
index 0fe2ba241e398cb6d7346d329f6196cdde9932ad..45851eca81544054a45a17aecc5fc6cfa329b5e2 100644 (file)
@@ -1046,10 +1046,12 @@ ALL_LINKERFLAGS = $(ALL_CXXFLAGS)
 
 # Build and host support libraries.
 
-# Use the "pic" build of libiberty if --enable-host-shared.
+# Use the "pic" build of libiberty if --enable-host-shared, unless we are
+# building for mingw.
+LIBIBERTY_PICDIR=$(if $(findstring mingw,$(target)),,pic)
 ifeq ($(enable_host_shared),yes)
-LIBIBERTY = ../libiberty/pic/libiberty.a
-BUILD_LIBIBERTY = $(build_libobjdir)/libiberty/pic/libiberty.a
+LIBIBERTY = ../libiberty/$(LIBIBERTY_PICDIR)/libiberty.a
+BUILD_LIBIBERTY = $(build_libobjdir)/libiberty/$(LIBIBERTY_PICDIR)/libiberty.a
 else
 LIBIBERTY = ../libiberty/libiberty.a
 BUILD_LIBIBERTY = $(build_libobjdir)/libiberty/libiberty.a
@@ -1726,7 +1728,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
 # This symlink makes the full installation name of the driver be available
 # from within the *build* directory, for use when running the JIT library
 # from there (e.g. when running its testsuite).
-$(FULL_DRIVER_NAME): ./xgcc
+$(FULL_DRIVER_NAME): ./xgcc$(exeext)
        rm -f $@
        $(LN_S) $< $@
 
index 8944b9b9f03625704f5237d5b37ac67eb1983d85..7efc7c2c332cd841de85faee9d0082e9ae04d80b 100644 (file)
@@ -162,7 +162,7 @@ c.install-plugin: installdirs
 # Install import library.
 ifeq ($(plugin_implib),yes)
        $(mkinstalldirs) $(DESTDIR)$(plugin_resourcesdir)
-       $(INSTALL_DATA) cc1$(exeext).a $(DESTDIR)/$(plugin_resourcesdir)/cc1$(exeext).a
+       $(INSTALL_DATA) cc1$(exeext).a $(DESTDIR)$(plugin_resourcesdir)/cc1$(exeext).a
 endif
 
 c.uninstall:
index 38ddfad28899957612657e9508545f6db003caf9..9cb7814d6d57caa0ed53d6e8019e5ab242ef543b 100644 (file)
 # into the jit rule, but that needs a little bit of work
 # to do the right thing within all.cross.
 
+ifneq (,$(findstring mingw,$(target)))
+LIBGCCJIT_FILENAME = libgccjit.dll
+
+jit: $(LIBGCCJIT_FILENAME) \
+       $(FULL_DRIVER_NAME)
+
+else
+
 LIBGCCJIT_LINKER_NAME = libgccjit.so
 LIBGCCJIT_VERSION_NUM = 0
 LIBGCCJIT_MINOR_NUM = 0
 LIBGCCJIT_RELEASE_NUM = 1
+
 LIBGCCJIT_SONAME = $(LIBGCCJIT_LINKER_NAME).$(LIBGCCJIT_VERSION_NUM)
 LIBGCCJIT_FILENAME = \
   $(LIBGCCJIT_SONAME).$(LIBGCCJIT_MINOR_NUM).$(LIBGCCJIT_RELEASE_NUM)
@@ -68,6 +77,7 @@ jit: $(LIBGCCJIT_FILENAME) \
        $(LIBGCCJIT_SYMLINK) \
        $(LIBGCCJIT_LINKER_NAME_SYMLINK) \
        $(FULL_DRIVER_NAME)
+endif
 
 # Tell GNU make to ignore these if they exist.
 .PHONY: jit
@@ -84,9 +94,21 @@ jit_OBJS = attribs.o \
        jit/jit-spec.o \
        gcc.o
 
+ifneq (,$(findstring mingw,$(target)))
+jit_OBJS += jit/jit-w32.o
+endif
+
 # Use strict warnings for this front end.
 jit-warn = $(STRICT_WARN)
 
+ifneq (,$(findstring mingw,$(target)))
+# Create import library libgccjit.dll.a
+LIBGCCJIT_EXTRA_OPTS = -Wl,--out-implib,$(LIBGCCJIT_FILENAME).a
+else
+LIBGCCJIT_EXTRA_OPTS = $(LIBGCCJIT_VERSION_SCRIPT_OPTION) \
+       $(LIBGCCJIT_SONAME_OPTION)
+endif
+
 # We avoid using $(BACKEND) from Makefile.in in order to avoid pulling
 # in main.o
 $(LIBGCCJIT_FILENAME): $(jit_OBJS) \
@@ -98,14 +120,16 @@ $(LIBGCCJIT_FILENAME): $(jit_OBJS) \
             $(jit_OBJS) libbackend.a libcommon-target.a libcommon.a \
             $(CPPLIB) $(LIBDECNUMBER) $(EXTRA_GCC_LIBS) $(LIBS) $(BACKENDLIBS) \
             $(EXTRA_GCC_OBJS) \
-            $(LIBGCCJIT_VERSION_SCRIPT_OPTION) \
-            $(LIBGCCJIT_SONAME_OPTION)
+            $(LIBGCCJIT_EXTRA_OPTS)
 
+# Create symlinks when not building for Windows
+ifeq (,$(findstring mingw,$(target)))
 $(LIBGCCJIT_SONAME_SYMLINK): $(LIBGCCJIT_FILENAME)
        ln -sf $(LIBGCCJIT_FILENAME) $(LIBGCCJIT_SONAME_SYMLINK)
 
 $(LIBGCCJIT_LINKER_NAME_SYMLINK): $(LIBGCCJIT_SONAME_SYMLINK)
        ln -sf $(LIBGCCJIT_SONAME_SYMLINK) $(LIBGCCJIT_LINKER_NAME_SYMLINK)
+endif
 
 #\f
 # Build hooks:
@@ -275,19 +299,31 @@ selftest-jit:
 
 #\f
 # Install hooks:
-jit.install-common: installdirs
+jit.install-headers:
+       $(INSTALL_DATA) $(srcdir)/jit/libgccjit.h \
+         $(DESTDIR)$(includedir)/libgccjit.h
+       $(INSTALL_DATA) $(srcdir)/jit/libgccjit++.h \
+         $(DESTDIR)$(includedir)/libgccjit++.h
+
+ifneq (,$(findstring mingw,$(target)))
+jit.install-common: installdirs jit.install-headers
+# Install import library
+       $(INSTALL_PROGRAM) $(LIBGCCJIT_FILENAME).a \
+         $(DESTDIR)$(libdir)/$(LIBGCCJIT_FILENAME).a
+# Install DLL file
        $(INSTALL_PROGRAM) $(LIBGCCJIT_FILENAME) \
-         $(DESTDIR)/$(libdir)/$(LIBGCCJIT_FILENAME)
+         $(DESTDIR)$(bindir)/$(LIBGCCJIT_FILENAME)
+else
+jit.install-common: installdirs jit.install-headers
+       $(INSTALL_PROGRAM) $(LIBGCCJIT_FILENAME) \
+         $(DESTDIR)$(libdir)/$(LIBGCCJIT_FILENAME)
        ln -sf \
          $(LIBGCCJIT_FILENAME) \
-         $(DESTDIR)/$(libdir)/$(LIBGCCJIT_SONAME_SYMLINK)
+         $(DESTDIR)$(libdir)/$(LIBGCCJIT_SONAME_SYMLINK)
        ln -sf \
          $(LIBGCCJIT_SONAME_SYMLINK)\
-         $(DESTDIR)/$(libdir)/$(LIBGCCJIT_LINKER_NAME_SYMLINK)
-       $(INSTALL_DATA) $(srcdir)/jit/libgccjit.h \
-         $(DESTDIR)/$(includedir)/libgccjit.h
-       $(INSTALL_DATA) $(srcdir)/jit/libgccjit++.h \
-         $(DESTDIR)/$(includedir)/libgccjit++.h
+         $(DESTDIR)$(libdir)/$(LIBGCCJIT_LINKER_NAME_SYMLINK)
+endif
 
 jit.install-man:
 
index 89e3bfe0a51dac6fcf6c624ea73e03ae18beb4a6..f8ef6042b396fa7c27d5c71a659d6d9d116b0f2c 100644 (file)
@@ -32,7 +32,7 @@ target_libs=""
 gtfiles="\$(srcdir)/jit/dummy-frontend.c"
 
 # The configuration requires --enable-host-shared
-# for jit to be supported.
+# for jit to be supported (only when not building for Mingw).
 # Hence to get the jit, one must configure with:
 #   --enable-host-shared --enable-languages=jit
 build_by_default="no"
index d2c8bb4c154d6e96644cf0505facc7026c720995..0fddf04da873a98a0004694e58f141da9b2d68a5 100644 (file)
@@ -47,6 +47,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-builtins.h"
 #include "jit-tempdir.h"
 
+#ifdef _WIN32
+#include "jit-w32.h"
+#endif
+
 /* Compare with gcc/c-family/c-common.h: DECL_C_BIT_FIELD,
    SET_DECL_C_BIT_FIELD.
    These are redefined here to avoid depending from the C frontend.  */
@@ -2159,8 +2163,10 @@ playback::compile_to_file::copy_file (const char *src_path,
 
   gcc_assert (total_sz_in == total_sz_out);
   if (get_logger ())
-    get_logger ()->log ("total bytes copied: %ld", total_sz_out);
+    get_logger ()->log ("total bytes copied: %zu", total_sz_out);
 
+  /* fchmod does not exist in Windows. */
+#ifndef _WIN32
   /* Set the permissions of the copy to those of the original file,
      in particular the "executable" bits.  */
   if (fchmod (fileno (f_out), stat_buf.st_mode) == -1)
@@ -2168,6 +2174,7 @@ playback::compile_to_file::copy_file (const char *src_path,
               "error setting mode of %s: %s",
               dst_path,
               xstrerror (errno));
+#endif
 
   fclose (f_out);
 }
@@ -2644,10 +2651,19 @@ dlopen_built_dso ()
 {
   JIT_LOG_SCOPE (get_logger ());
   auto_timevar load_timevar (get_timer (), TV_LOAD);
-  void *handle = NULL;
-  const char *error = NULL;
+  result::handle handle = NULL;
   result *result_obj = NULL;
 
+#ifdef _WIN32
+  /* Clear any existing error.  */
+  SetLastError(0);
+
+  handle = LoadLibrary(m_tempdir->get_path_so_file ());
+  if (GetLastError() != 0)  {
+    print_last_error();
+  }
+#else
+  const char *error = NULL;
   /* Clear any existing error.  */
   dlerror ();
 
@@ -2656,6 +2672,8 @@ dlopen_built_dso ()
   if ((error = dlerror()) != NULL)  {
     add_error (NULL, "%s", error);
   }
+#endif
+
   if (handle)
     {
       /* We've successfully dlopened the result; create a
index c10e5a13c305fc8f60b5818ab4ffd1cd50f6aee4..633626412168c52a5f5d216aeb50d0bde5f59e0f 100644 (file)
@@ -27,13 +27,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-result.h"
 #include "jit-tempdir.h"
 
+#ifdef _WIN32
+#include "jit-w32.h"
+#endif
+
 namespace gcc {
 namespace jit {
 
 /* Constructor for gcc::jit::result.  */
 
 result::
-result(logger *logger, void *dso_handle, tempdir *tempdir_) :
+result(logger *logger, handle dso_handle, tempdir *tempdir_) :
   log_user (logger),
   m_dso_handle (dso_handle),
   m_tempdir (tempdir_)
@@ -49,8 +53,11 @@ result::~result()
 {
   JIT_LOG_SCOPE (get_logger ());
 
+#ifdef _WIN32
+  FreeLibrary(m_dso_handle);
+#else
   dlclose (m_dso_handle);
-
+#endif
   /* Responsibility for cleaning up the tempdir (including "fake.so" within
      the filesystem) might have been handed to us by the playback::context,
      so that the cleanup can be delayed (see PR jit/64206).
@@ -72,8 +79,17 @@ get_code (const char *funcname)
   JIT_LOG_SCOPE (get_logger ());
 
   void *code;
-  const char *error;
 
+#ifdef _WIN32
+  /* Clear any existing error.  */
+  SetLastError(0);
+
+  code = (void *)GetProcAddress(m_dso_handle, funcname);
+  if (GetLastError() != 0)  {
+    print_last_error ();
+  }
+#else
+  const char *error;
   /* Clear any existing error.  */
   dlerror ();
 
@@ -82,6 +98,7 @@ get_code (const char *funcname)
   if ((error = dlerror()) != NULL)  {
     fprintf(stderr, "%s\n", error);
   }
+#endif
 
   return code;
 }
@@ -99,8 +116,17 @@ get_global (const char *name)
   JIT_LOG_SCOPE (get_logger ());
 
   void *global;
-  const char *error;
 
+#ifdef _WIN32
+  /* Clear any existing error.  */
+  SetLastError(0);
+
+  global = (void *)GetProcAddress(m_dso_handle, name);
+  if (GetLastError() != 0)  {
+    print_last_error ();
+  }
+#else
+  const char *error;
   /* Clear any existing error.  */
   dlerror ();
 
@@ -109,6 +135,7 @@ get_global (const char *name)
   if ((error = dlerror()) != NULL)  {
     fprintf(stderr, "%s\n", error);
   }
+#endif
 
   return global;
 }
index 02586060368bc170b00a8142fd03452004b4245f..d8e940aafb0be1c073e583ab06243baf2e2e8149 100644 (file)
@@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef JIT_RESULT_H
 #define JIT_RESULT_H
 
+#ifdef _WIN32
+#include <minwindef.h>
+#endif
+
 namespace gcc {
 
 namespace jit {
@@ -29,7 +33,13 @@ namespace jit {
 class result : public log_user
 {
 public:
-  result(logger *logger, void *dso_handle, tempdir *tempdir_);
+#ifdef _WIN32
+  typedef HMODULE handle;
+#else
+  typedef void* handle;
+#endif
+
+  result(logger *logger, handle dso_handle, tempdir *tempdir_);
 
   virtual ~result();
 
@@ -40,7 +50,7 @@ public:
   get_global (const char *name);
 
 private:
-  void *m_dso_handle;
+  handle m_dso_handle;
   tempdir *m_tempdir;
 };
 
index 10c528faf2887aa97d84b3a3b36c94c97324da09..050d53409caf1948c24aea14e79cc9f3ec1aff1d 100644 (file)
@@ -24,7 +24,11 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "jit-tempdir.h"
 
+#ifdef _WIN32
+#include "jit-w32.h"
+#endif
 
+#ifndef _WIN32
 /* Construct a tempdir path template suitable for use by mkdtemp
    e.g. "/tmp/libgccjit-XXXXXX", but respecting the rules in
    libiberty's choose_tempdir rather than hardcoding "/tmp/".
@@ -62,6 +66,7 @@ make_tempdir_path_template ()
 
   return result;
 }
+#endif
 
 /* The constructor for the jit::tempdir object.
    The real work is done by the jit::tempdir::create method.  */
@@ -87,6 +92,9 @@ gcc::jit::tempdir::create ()
 {
   JIT_LOG_SCOPE (get_logger ());
 
+#ifdef _WIN32
+  m_path_tempdir = win_mkdtemp ();
+#else
   m_path_template = make_tempdir_path_template ();
   if (!m_path_template)
     return false;
@@ -97,6 +105,8 @@ gcc::jit::tempdir::create ()
      is unique.  Hence no other (non-root) users should have access to
      the paths within it.  */
   m_path_tempdir = mkdtemp (m_path_template);
+#endif
+
   if (!m_path_tempdir)
     return false;
   log ("m_path_tempdir: %s", m_path_tempdir);
diff --git a/gcc/jit/jit-w32.c b/gcc/jit/jit-w32.c
new file mode 100644 (file)
index 0000000..f24f4f0
--- /dev/null
@@ -0,0 +1,255 @@
+/* Functions used by the Windows port of libgccjit.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Contributed by Nicolas Bertolo <nicolasbertolo@gmail.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+
+/* Required for rand_s */
+#define _CRT_RAND_S
+
+#include <cstdio>
+#include <cstdint>
+
+#include "jit-w32.h"
+
+#include "libiberty.h"
+
+#include <accctrl.h>
+#include <aclapi.h>
+
+namespace gcc {
+namespace jit {
+void
+print_last_error (void)
+{
+  LPSTR psz = NULL;
+  DWORD dwErrorCode;
+  dwErrorCode = GetLastError();
+  const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+                                     | FORMAT_MESSAGE_IGNORE_INSERTS
+                                     | FORMAT_MESSAGE_ALLOCATE_BUFFER
+                                     | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+                                     NULL,
+                                     dwErrorCode,
+                                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                                     reinterpret_cast<LPSTR>(&psz),
+                                     0,
+                                     NULL);
+  if (cchMsg > 0)
+    {
+      fprintf (stderr, "%s\n", psz);
+      LocalFree (psz);
+    }
+  else
+    {
+      fprintf (stderr, "Failed to retrieve error message string for error %lu\n",
+               dwErrorCode);
+    }
+}
+
+/* Helper function used for getting the SID belonging to the current user. */
+static TOKEN_USER*
+get_TOKEN_USER_current_user ()
+{
+  TOKEN_USER *result = NULL;
+
+  HANDLE process_token = INVALID_HANDLE_VALUE;
+
+  DWORD token_user_info_len;
+  TOKEN_USER *token_user = NULL;
+
+  /* Get current process access token. */
+  if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ,
+                         &process_token))
+    return NULL;
+
+  /* Get necessary buffer size. */
+  if (!GetTokenInformation(process_token, TokenUser, NULL, 0, &token_user_info_len)
+      && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    goto cleanup;
+
+  token_user = (TOKEN_USER*) new char[token_user_info_len];
+
+  /* Get info about the user of the process */
+  if (!GetTokenInformation (process_token, TokenUser, token_user,
+                            token_user_info_len, &token_user_info_len))
+      goto cleanup;
+
+  result = token_user;
+
+ cleanup:
+  if (process_token != INVALID_HANDLE_VALUE)
+    CloseHandle(process_token);
+
+  if (token_user != NULL && result == NULL)
+    delete[] (char*)token_user;
+
+  return result;
+}
+
+/* Helper function to create a directory with permissions such that only the
+  current user can access it. */
+static bool
+create_directory_for_current_user (const char * path)
+{
+  PACL pACL = NULL;
+  EXPLICIT_ACCESS ea;
+  SECURITY_ATTRIBUTES sa;
+  SECURITY_DESCRIPTOR SD;
+  DWORD dwRes;
+  bool result = true;
+  TOKEN_USER *token_user = NULL;
+
+  token_user = get_TOKEN_USER_current_user();
+  if (!token_user)
+    return false;
+
+  memset (&ea, 0, sizeof (EXPLICIT_ACCESS));
+  ea.grfAccessPermissions = GENERIC_ALL; /* Access to all. */
+  ea.grfAccessMode = SET_ACCESS; /* Set access and revoke everything else. */
+  /* This is necessary for the Windows Explorer GUI to show the correct tick
+     boxes in the "Security" tab. */
+  ea.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
+  ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+  ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
+  ea.Trustee.ptstrName = (char*) token_user->User.Sid;
+
+  /* Create a new ACL that contains the new ACEs. */
+  dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
+  if (dwRes != ERROR_SUCCESS)
+    return false;
+
+  if (!InitializeSecurityDescriptor (&SD,
+                                     SECURITY_DESCRIPTOR_REVISION))
+    goto cleanup;
+
+  /* Add the ACL to the security descriptor. */
+  if (!SetSecurityDescriptorDacl (&SD,
+                                  TRUE,     /* use pACL */
+                                  pACL,
+                                  FALSE))   /* not a default DACL */
+    goto cleanup;
+
+  /* Initialize a security attributes structure. */
+  sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+  sa.lpSecurityDescriptor = &SD;
+  sa.bInheritHandle = FALSE;
+
+  /* Finally create the directory */
+  if (!CreateDirectoryA (path, &sa))
+    result = false;
+
+ cleanup:
+  if (pACL)
+    LocalFree (pACL);
+
+  if (token_user)
+    delete[] (char*)token_user;
+
+  return result;
+}
+
+
+char *
+win_mkdtemp (void)
+{
+  char lpTempPathBuffer[MAX_PATH];
+
+  /* Gets the temp path env string (no guarantee it's a valid path). */
+  DWORD dwRetVal = GetTempPath (MAX_PATH, lpTempPathBuffer);
+  if (dwRetVal > MAX_PATH || (dwRetVal == 0))
+    {
+      print_last_error ();
+      return NULL;
+    }
+
+  /* Check that the directory actually exists. */
+  DWORD dwAttrib = GetFileAttributes (lpTempPathBuffer);
+  bool temp_path_exists = (dwAttrib != INVALID_FILE_ATTRIBUTES
+                           && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+  if (!temp_path_exists)
+    {
+      fprintf (stderr, "Path returned by GetTempPath does not exist: %s\n",
+               lpTempPathBuffer);
+    }
+
+  /* Make sure there is enough space in the buffer for the prefix and random
+     number.*/
+  int temp_path_buffer_len = dwRetVal;
+  const int appended_len = strlen ("\\libgccjit-123456");
+  if (temp_path_buffer_len + appended_len + 1 >= MAX_PATH)
+    {
+      fprintf (stderr, "Temporary file path too long for generation of random"
+               " directories: %s", lpTempPathBuffer);
+    }
+
+  /* This is all the space we have in the buffer to store the random number and
+     prefix. */
+  int extraspace = MAX_PATH - temp_path_buffer_len - 1;
+
+  int tries;
+  const int max_tries = 1000;
+
+  for (tries = 0; tries < max_tries; ++tries)
+    {
+      /* Get a random number in [0; UINT_MAX]. */
+      unsigned int rand_num;
+      if (rand_s (&rand_num) != 0)
+        {
+          fprintf (stderr,
+                   "Failed to create a random number using rand_s(): %s\n",
+                   _strerror (NULL));
+          return NULL;
+        }
+
+      /* Create 6 digits random number. */
+      rand_num = ((double)rand_num / ((double) UINT_MAX + 1 ) * 1000000);
+
+      /* Copy the prefix and random number to the buffer. */
+      snprintf (&lpTempPathBuffer[temp_path_buffer_len], extraspace,
+                "\\libgccjit-%06u", rand_num);
+
+      if (create_directory_for_current_user (lpTempPathBuffer))
+        break; // success!
+
+      /* If we can't create the directory because we got unlucky and the
+         directory already exists retry, otherwise fail. */
+      if (GetLastError () != ERROR_ALREADY_EXISTS)
+        {
+          print_last_error ();
+          return NULL;
+        }
+    }
+
+  if (tries == max_tries)
+    {
+      fprintf (stderr, "Failed to create a random directory in %s\n",
+               lpTempPathBuffer);
+      return NULL;
+    }
+
+  {
+    int allocate_len = temp_path_buffer_len + appended_len + 1;
+    char * result = XNEWVEC (char, allocate_len);
+    strcpy (result, lpTempPathBuffer);
+    return result;
+  }
+}
+}
+}
diff --git a/gcc/jit/jit-w32.h b/gcc/jit/jit-w32.h
new file mode 100644 (file)
index 0000000..c6e30ea
--- /dev/null
@@ -0,0 +1,30 @@
+/* Functions used by the Windows port of libgccjit.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Contributed by Nicolas Bertolo <nicolasbertolo@gmail.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+
+#include <windows.h>
+
+namespace gcc {
+namespace jit {
+extern void print_last_error (void);
+extern char * win_mkdtemp(void);
+}
+}